Merge "Fix potential memory leaks"
diff --git a/drm/libmediadrm/Android.bp b/drm/libmediadrm/Android.bp
index 408d216..1667d5b 100644
--- a/drm/libmediadrm/Android.bp
+++ b/drm/libmediadrm/Android.bp
@@ -74,6 +74,7 @@
static_libs: [
"resourcemanager_aidl_interface-ndk",
"libaidlcommonsupport",
+ "libjsoncpp",
],
export_shared_lib_headers: [
diff --git a/drm/libmediadrm/DrmUtils.cpp b/drm/libmediadrm/DrmUtils.cpp
index be0cd4b..c144fce 100644
--- a/drm/libmediadrm/DrmUtils.cpp
+++ b/drm/libmediadrm/DrmUtils.cpp
@@ -32,6 +32,7 @@
#include <android/hardware/drm/1.4/IDrmFactory.h>
#include <android/hidl/manager/1.2/IServiceManager.h>
#include <hidl/HidlSupport.h>
+#include <json/json.h>
#include <cutils/properties.h>
#include <utils/Errors.h>
@@ -410,6 +411,165 @@
return logs;
}
+DrmStatus statusAidlToDrmStatus(::ndk::ScopedAStatus& statusAidl) {
+ if (statusAidl.isOk()) return OK;
+ if (statusAidl.getExceptionCode() != EX_SERVICE_SPECIFIC) return DEAD_OBJECT;
+ auto astatus = static_cast<StatusAidl>(statusAidl.getServiceSpecificError());
+ status_t status{};
+ switch (astatus) {
+ case StatusAidl::OK:
+ status = OK;
+ break;
+ case StatusAidl::BAD_VALUE:
+ status = BAD_VALUE;
+ break;
+ case StatusAidl::ERROR_DRM_CANNOT_HANDLE:
+ status = ERROR_DRM_CANNOT_HANDLE;
+ break;
+ case StatusAidl::ERROR_DRM_DECRYPT:
+ status = ERROR_DRM_DECRYPT;
+ break;
+ case StatusAidl::ERROR_DRM_DEVICE_REVOKED:
+ status = ERROR_DRM_DEVICE_REVOKED;
+ break;
+ case StatusAidl::ERROR_DRM_FRAME_TOO_LARGE:
+ status = ERROR_DRM_FRAME_TOO_LARGE;
+ break;
+ case StatusAidl::ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION:
+ status = ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION;
+ break;
+ case StatusAidl::ERROR_DRM_INSUFFICIENT_SECURITY:
+ status = ERROR_DRM_INSUFFICIENT_SECURITY;
+ break;
+ case StatusAidl::ERROR_DRM_INVALID_STATE:
+ status = ERROR_DRM_INVALID_STATE;
+ break;
+ case StatusAidl::ERROR_DRM_LICENSE_EXPIRED:
+ status = ERROR_DRM_LICENSE_EXPIRED;
+ break;
+ case StatusAidl::ERROR_DRM_NO_LICENSE:
+ status = ERROR_DRM_NO_LICENSE;
+ break;
+ case StatusAidl::ERROR_DRM_NOT_PROVISIONED:
+ status = ERROR_DRM_NOT_PROVISIONED;
+ break;
+ case StatusAidl::ERROR_DRM_RESOURCE_BUSY:
+ status = ERROR_DRM_RESOURCE_BUSY;
+ break;
+ case StatusAidl::ERROR_DRM_RESOURCE_CONTENTION:
+ status = ERROR_DRM_RESOURCE_CONTENTION;
+ break;
+ case StatusAidl::ERROR_DRM_SESSION_LOST_STATE:
+ status = ERROR_DRM_SESSION_LOST_STATE;
+ break;
+ case StatusAidl::ERROR_DRM_SESSION_NOT_OPENED:
+ status = ERROR_DRM_SESSION_NOT_OPENED;
+ break;
+
+ // New in S / drm@1.4:
+ case StatusAidl::CANNOT_DECRYPT_ZERO_SUBSAMPLES:
+ status = ERROR_DRM_ZERO_SUBSAMPLES;
+ break;
+ case StatusAidl::CRYPTO_LIBRARY_ERROR:
+ status = ERROR_DRM_CRYPTO_LIBRARY;
+ break;
+ case StatusAidl::GENERAL_OEM_ERROR:
+ status = ERROR_DRM_GENERIC_OEM;
+ break;
+ case StatusAidl::GENERAL_PLUGIN_ERROR:
+ status = ERROR_DRM_GENERIC_PLUGIN;
+ break;
+ case StatusAidl::INIT_DATA_INVALID:
+ status = ERROR_DRM_INIT_DATA;
+ break;
+ case StatusAidl::KEY_NOT_LOADED:
+ status = ERROR_DRM_KEY_NOT_LOADED;
+ break;
+ case StatusAidl::LICENSE_PARSE_ERROR:
+ status = ERROR_DRM_LICENSE_PARSE;
+ break;
+ case StatusAidl::LICENSE_POLICY_ERROR:
+ status = ERROR_DRM_LICENSE_POLICY;
+ break;
+ case StatusAidl::LICENSE_RELEASE_ERROR:
+ status = ERROR_DRM_LICENSE_RELEASE;
+ break;
+ case StatusAidl::LICENSE_REQUEST_REJECTED:
+ status = ERROR_DRM_LICENSE_REQUEST_REJECTED;
+ break;
+ case StatusAidl::LICENSE_RESTORE_ERROR:
+ status = ERROR_DRM_LICENSE_RESTORE;
+ break;
+ case StatusAidl::LICENSE_STATE_ERROR:
+ status = ERROR_DRM_LICENSE_STATE;
+ break;
+ case StatusAidl::MALFORMED_CERTIFICATE:
+ status = ERROR_DRM_CERTIFICATE_MALFORMED;
+ break;
+ case StatusAidl::MEDIA_FRAMEWORK_ERROR:
+ status = ERROR_DRM_MEDIA_FRAMEWORK;
+ break;
+ case StatusAidl::MISSING_CERTIFICATE:
+ status = ERROR_DRM_CERTIFICATE_MISSING;
+ break;
+ case StatusAidl::PROVISIONING_CERTIFICATE_ERROR:
+ status = ERROR_DRM_PROVISIONING_CERTIFICATE;
+ break;
+ case StatusAidl::PROVISIONING_CONFIGURATION_ERROR:
+ status = ERROR_DRM_PROVISIONING_CONFIG;
+ break;
+ case StatusAidl::PROVISIONING_PARSE_ERROR:
+ status = ERROR_DRM_PROVISIONING_PARSE;
+ break;
+ case StatusAidl::PROVISIONING_REQUEST_REJECTED:
+ status = ERROR_DRM_PROVISIONING_REQUEST_REJECTED;
+ break;
+ case StatusAidl::RETRYABLE_PROVISIONING_ERROR:
+ status = ERROR_DRM_PROVISIONING_RETRY;
+ break;
+ case StatusAidl::SECURE_STOP_RELEASE_ERROR:
+ status = ERROR_DRM_SECURE_STOP_RELEASE;
+ break;
+ case StatusAidl::STORAGE_READ_FAILURE:
+ status = ERROR_DRM_STORAGE_READ;
+ break;
+ case StatusAidl::STORAGE_WRITE_FAILURE:
+ status = ERROR_DRM_STORAGE_WRITE;
+ break;
+
+ case StatusAidl::ERROR_DRM_UNKNOWN:
+ default:
+ status = ERROR_DRM_UNKNOWN;
+ break;
+ }
+
+ Json::Value errorDetails;
+ Json::Reader reader;
+ if (!reader.parse(statusAidl.getMessage(), errorDetails)) {
+ return status;
+ }
+
+ int32_t cdmErr{}, oemErr{}, ctx{};
+ std::string errMsg;
+ auto val = errorDetails["cdmError"];
+ if (!val.isNull()) {
+ cdmErr = val.asInt();
+ }
+ val = errorDetails["oemError"];
+ if (!val.isNull()) {
+ oemErr = val.asInt();
+ }
+ val = errorDetails["context"];
+ if (!val.isNull()) {
+ ctx = val.asInt();
+ }
+ val = errorDetails["errorMessage"];
+ if (!val.isNull()) {
+ errMsg = val.asString();
+ }
+ return DrmStatus(status, cdmErr, oemErr, ctx, errMsg);
+}
+
LogBuffer gLogBuf;
} // namespace DrmUtils
} // namespace android
diff --git a/drm/libmediadrm/fuzzer/Android.bp b/drm/libmediadrm/fuzzer/Android.bp
index a85e3cf..deda9ef 100644
--- a/drm/libmediadrm/fuzzer/Android.bp
+++ b/drm/libmediadrm/fuzzer/Android.bp
@@ -37,6 +37,7 @@
"liblog",
"resourcemanager_aidl_interface-ndk",
"libaidlcommonsupport",
+ "libjsoncpp",
],
header_libs: [
"libmedia_headers",
diff --git a/drm/libmediadrm/include/mediadrm/DrmStatus.h b/drm/libmediadrm/include/mediadrm/DrmStatus.h
index 4f29a5a..1155af6 100644
--- a/drm/libmediadrm/include/mediadrm/DrmStatus.h
+++ b/drm/libmediadrm/include/mediadrm/DrmStatus.h
@@ -16,27 +16,34 @@
#ifndef DRM_STATUS_
#define DRM_STATUS_
-#include <stdint.h>
#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/MediaErrors.h>
#include <utils/Errors.h>
+#include <stdint.h>
+#include <string>
+
namespace android {
struct DrmStatus {
public:
- DrmStatus(status_t status, int32_t cdmerr = 0, int32_t oemerr = 0)
- : mStatus(status), mCdmErr(cdmerr), mOemErr(oemerr) {}
+ DrmStatus(status_t status, int32_t cdmErr = 0, int32_t oemErr = 0,
+ int32_t ctx = 0, std::string errMsg = "")
+ : mStatus(status), mCdmErr(cdmErr), mOemErr(oemErr),
+ mCtx(ctx), mErrMsg(errMsg) {}
operator status_t() const { return mStatus; }
- int32_t cdmErr() const { return mCdmErr; }
- int32_t oemErr() const { return mOemErr; }
+ int32_t getCdmErr() const { return mCdmErr; }
+ int32_t getOemErr() const { return mOemErr; }
+ int32_t getContext() const { return mCtx; }
+ std::string getErrorMessage() const { return mErrMsg; }
bool operator==(status_t other) const { return mStatus == other; }
bool operator!=(status_t other) const { return mStatus != other; }
private:
status_t mStatus;
- int32_t mCdmErr{}, mOemErr{};
+ int32_t mCdmErr{}, mOemErr{}, mCtx{};
+ std::string mErrMsg;
};
} // namespace android
diff --git a/drm/libmediadrm/interface/mediadrm/DrmUtils.h b/drm/libmediadrm/interface/mediadrm/DrmUtils.h
index 32390d6..be16747 100644
--- a/drm/libmediadrm/interface/mediadrm/DrmUtils.h
+++ b/drm/libmediadrm/interface/mediadrm/DrmUtils.h
@@ -199,98 +199,7 @@
return toStatusT_1_4(err);
}
-inline DrmStatus statusAidlToDrmStatus(::ndk::ScopedAStatus& statusAidl) {
- if (statusAidl.isOk()) return OK;
- if (statusAidl.getExceptionCode() != EX_SERVICE_SPECIFIC) return DEAD_OBJECT;
- auto status = static_cast<StatusAidl>(statusAidl.getServiceSpecificError());
- switch (status) {
- case StatusAidl::OK:
- return OK;
- case StatusAidl::BAD_VALUE:
- return BAD_VALUE;
- case StatusAidl::ERROR_DRM_CANNOT_HANDLE:
- return ERROR_DRM_CANNOT_HANDLE;
- case StatusAidl::ERROR_DRM_DECRYPT:
- return ERROR_DRM_DECRYPT;
- case StatusAidl::ERROR_DRM_DEVICE_REVOKED:
- return ERROR_DRM_DEVICE_REVOKED;
- case StatusAidl::ERROR_DRM_FRAME_TOO_LARGE:
- return ERROR_DRM_FRAME_TOO_LARGE;
- case StatusAidl::ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION:
- return ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION;
- case StatusAidl::ERROR_DRM_INSUFFICIENT_SECURITY:
- return ERROR_DRM_INSUFFICIENT_SECURITY;
- case StatusAidl::ERROR_DRM_INVALID_STATE:
- return ERROR_DRM_INVALID_STATE;
- case StatusAidl::ERROR_DRM_LICENSE_EXPIRED:
- return ERROR_DRM_LICENSE_EXPIRED;
- case StatusAidl::ERROR_DRM_NO_LICENSE:
- return ERROR_DRM_NO_LICENSE;
- case StatusAidl::ERROR_DRM_NOT_PROVISIONED:
- return ERROR_DRM_NOT_PROVISIONED;
- case StatusAidl::ERROR_DRM_RESOURCE_BUSY:
- return ERROR_DRM_RESOURCE_BUSY;
- case StatusAidl::ERROR_DRM_RESOURCE_CONTENTION:
- return ERROR_DRM_RESOURCE_CONTENTION;
- case StatusAidl::ERROR_DRM_SESSION_LOST_STATE:
- return ERROR_DRM_SESSION_LOST_STATE;
- case StatusAidl::ERROR_DRM_SESSION_NOT_OPENED:
- return ERROR_DRM_SESSION_NOT_OPENED;
-
- // New in S / drm@1.4:
- case StatusAidl::CANNOT_DECRYPT_ZERO_SUBSAMPLES:
- return ERROR_DRM_ZERO_SUBSAMPLES;
- case StatusAidl::CRYPTO_LIBRARY_ERROR:
- return ERROR_DRM_CRYPTO_LIBRARY;
- case StatusAidl::GENERAL_OEM_ERROR:
- return ERROR_DRM_GENERIC_OEM;
- case StatusAidl::GENERAL_PLUGIN_ERROR:
- return ERROR_DRM_GENERIC_PLUGIN;
- case StatusAidl::INIT_DATA_INVALID:
- return ERROR_DRM_INIT_DATA;
- case StatusAidl::KEY_NOT_LOADED:
- return ERROR_DRM_KEY_NOT_LOADED;
- case StatusAidl::LICENSE_PARSE_ERROR:
- return ERROR_DRM_LICENSE_PARSE;
- case StatusAidl::LICENSE_POLICY_ERROR:
- return ERROR_DRM_LICENSE_POLICY;
- case StatusAidl::LICENSE_RELEASE_ERROR:
- return ERROR_DRM_LICENSE_RELEASE;
- case StatusAidl::LICENSE_REQUEST_REJECTED:
- return ERROR_DRM_LICENSE_REQUEST_REJECTED;
- case StatusAidl::LICENSE_RESTORE_ERROR:
- return ERROR_DRM_LICENSE_RESTORE;
- case StatusAidl::LICENSE_STATE_ERROR:
- return ERROR_DRM_LICENSE_STATE;
- case StatusAidl::MALFORMED_CERTIFICATE:
- return ERROR_DRM_CERTIFICATE_MALFORMED;
- case StatusAidl::MEDIA_FRAMEWORK_ERROR:
- return ERROR_DRM_MEDIA_FRAMEWORK;
- case StatusAidl::MISSING_CERTIFICATE:
- return ERROR_DRM_CERTIFICATE_MISSING;
- case StatusAidl::PROVISIONING_CERTIFICATE_ERROR:
- return ERROR_DRM_PROVISIONING_CERTIFICATE;
- case StatusAidl::PROVISIONING_CONFIGURATION_ERROR:
- return ERROR_DRM_PROVISIONING_CONFIG;
- case StatusAidl::PROVISIONING_PARSE_ERROR:
- return ERROR_DRM_PROVISIONING_PARSE;
- case StatusAidl::PROVISIONING_REQUEST_REJECTED:
- return ERROR_DRM_PROVISIONING_REQUEST_REJECTED;
- case StatusAidl::RETRYABLE_PROVISIONING_ERROR:
- return ERROR_DRM_PROVISIONING_RETRY;
- case StatusAidl::SECURE_STOP_RELEASE_ERROR:
- return ERROR_DRM_SECURE_STOP_RELEASE;
- case StatusAidl::STORAGE_READ_FAILURE:
- return ERROR_DRM_STORAGE_READ;
- case StatusAidl::STORAGE_WRITE_FAILURE:
- return ERROR_DRM_STORAGE_WRITE;
-
- case StatusAidl::ERROR_DRM_UNKNOWN:
- default:
- return ERROR_DRM_UNKNOWN;
- }
- return ERROR_DRM_UNKNOWN;
-}
+DrmStatus statusAidlToDrmStatus(::ndk::ScopedAStatus& statusAidl);
template<typename T, typename U>
status_t GetLogMessagesAidl(const std::shared_ptr<U> &obj, Vector<::V1_4::LogMessage> &logs) {
diff --git a/media/libaudioclient/AidlConversion.cpp b/media/libaudioclient/AidlConversion.cpp
index 90d51fd..d32788a 100644
--- a/media/libaudioclient/AidlConversion.cpp
+++ b/media/libaudioclient/AidlConversion.cpp
@@ -1527,6 +1527,8 @@
return AUDIO_OUTPUT_FLAG_ULTRASOUND;
case AudioOutputFlags::SPATIALIZER:
return AUDIO_OUTPUT_FLAG_SPATIALIZER;
+ case AudioOutputFlags::BIT_PERFECT:
+ return AUDIO_OUTPUT_FLAG_BIT_PERFECT;
}
return unexpected(BAD_VALUE);
}
@@ -1572,6 +1574,8 @@
return AudioOutputFlags::ULTRASOUND;
case AUDIO_OUTPUT_FLAG_SPATIALIZER:
return AudioOutputFlags::SPATIALIZER;
+ case AUDIO_OUTPUT_FLAG_BIT_PERFECT:
+ return AudioOutputFlags::BIT_PERFECT;
}
return unexpected(BAD_VALUE);
}
diff --git a/media/libaudioclient/TEST_MAPPING b/media/libaudioclient/TEST_MAPPING
index 51080ef..10f9d9b 100644
--- a/media/libaudioclient/TEST_MAPPING
+++ b/media/libaudioclient/TEST_MAPPING
@@ -32,5 +32,10 @@
{
"name": "audiosystem_tests"
}
+ ],
+ "postsubmit": [
+ {
+ "name": "audioeffect_analysis"
+ }
]
}
diff --git a/media/libaudioclient/tests/Android.bp b/media/libaudioclient/tests/Android.bp
index 1b09173..df1506a 100644
--- a/media/libaudioclient/tests/Android.bp
+++ b/media/libaudioclient/tests/Android.bp
@@ -176,6 +176,22 @@
}
cc_test {
+ name: "audioeffect_analysis",
+ defaults: ["libaudioclient_gtests_defaults"],
+ // flag needed for pfft/pffft.hpp
+ cflags: [
+ "-Wno-error=unused-parameter",
+ ],
+ srcs: [
+ "audioeffect_analyser.cpp",
+ "audio_test_utils.cpp",
+ ],
+ static_libs: [
+ "libpffft",
+ ],
+}
+
+cc_test {
name: "audiorouting_tests",
defaults: ["libaudioclient_gtests_defaults"],
srcs: [
diff --git a/media/libaudioclient/tests/audio_test_utils.cpp b/media/libaudioclient/tests/audio_test_utils.cpp
index 850eb34..1e26ff6 100644
--- a/media/libaudioclient/tests/audio_test_utils.cpp
+++ b/media/libaudioclient/tests/audio_test_utils.cpp
@@ -17,11 +17,15 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "AudioTestUtils"
+#include <android-base/file.h>
#include <system/audio_config.h>
#include <utils/Log.h>
#include "audio_test_utils.h"
+#define WAIT_PERIOD_MS 10 // from AudioTrack.cpp
+#define MAX_WAIT_TIME_MS 5000
+
template <class T>
constexpr void (*xmlDeleter)(T* t);
template <>
@@ -37,12 +41,6 @@
return std::unique_ptr<T, decltype(deleter)>{t, deleter};
}
-// Generates a random string.
-void CreateRandomFile(int& fd) {
- std::string filename = "/data/local/tmp/record-XXXXXX";
- fd = mkstemp(filename.data());
-}
-
void OnAudioDeviceUpdateNotifier::onAudioDeviceUpdate(audio_io_handle_t audioIo,
audio_port_handle_t deviceId) {
std::unique_lock<std::mutex> lock{mMutex};
@@ -52,11 +50,14 @@
mCondition.notify_all();
}
-status_t OnAudioDeviceUpdateNotifier::waitForAudioDeviceCb() {
+status_t OnAudioDeviceUpdateNotifier::waitForAudioDeviceCb(audio_port_handle_t expDeviceId) {
std::unique_lock<std::mutex> lock{mMutex};
- if (mAudioIo == AUDIO_IO_HANDLE_NONE) {
+ if (mAudioIo == AUDIO_IO_HANDLE_NONE ||
+ (expDeviceId != AUDIO_PORT_HANDLE_NONE && expDeviceId != mDeviceId)) {
mCondition.wait_for(lock, std::chrono::milliseconds(500));
- if (mAudioIo == AUDIO_IO_HANDLE_NONE) return TIMED_OUT;
+ if (mAudioIo == AUDIO_IO_HANDLE_NONE ||
+ (expDeviceId != AUDIO_PORT_HANDLE_NONE && expDeviceId != mDeviceId))
+ return TIMED_OUT;
}
return OK;
}
@@ -167,15 +168,16 @@
}
status_t AudioPlayback::fillBuffer() {
- if (PLAY_STARTED != mState && PLAY_STOPPED != mState) return INVALID_OPERATION;
- int retry = 25;
+ if (PLAY_STARTED != mState) return INVALID_OPERATION;
+ const int maxTries = MAX_WAIT_TIME_MS / WAIT_PERIOD_MS;
+ int counter = 0;
uint8_t* ipBuffer = static_cast<uint8_t*>(static_cast<void*>(mMemory->unsecurePointer()));
size_t nonContig = 0;
size_t bytesAvailable = mMemCapacity - mBytesUsedSoFar;
while (bytesAvailable > 0) {
AudioTrack::Buffer trackBuffer;
trackBuffer.frameCount = mTrack->frameCount() * 2;
- status_t status = mTrack->obtainBuffer(&trackBuffer, retry, &nonContig);
+ status_t status = mTrack->obtainBuffer(&trackBuffer, 1, &nonContig);
if (OK == status) {
size_t bytesToCopy = std::min(bytesAvailable, trackBuffer.size());
if (bytesToCopy > 0) {
@@ -184,14 +186,11 @@
mTrack->releaseBuffer(&trackBuffer);
mBytesUsedSoFar += bytesToCopy;
bytesAvailable = mMemCapacity - mBytesUsedSoFar;
- if (bytesAvailable == 0) {
- stop();
- }
+ counter = 0;
} else if (WOULD_BLOCK == status) {
- if (mStopPlaying)
- return OK;
- else
- return TIMED_OUT;
+ // if not received a buffer for MAX_WAIT_TIME_MS, something has gone wrong
+ if (counter == maxTries) return TIMED_OUT;
+ counter++;
}
}
return OK;
@@ -199,14 +198,15 @@
status_t AudioPlayback::waitForConsumption(bool testSeek) {
if (PLAY_STARTED != mState) return INVALID_OPERATION;
- // in static buffer mode, lets not play clips with duration > 30 sec
- int retry = 300;
- // Total number of frames in the input file.
+
+ const int maxTries = MAX_WAIT_TIME_MS / WAIT_PERIOD_MS;
+ int counter = 0;
size_t totalFrameCount = mMemCapacity / mTrack->frameSize();
- while (!mStopPlaying && retry > 0) {
- // Get the total numbers of frames played.
+ while (!mStopPlaying && counter < maxTries) {
uint32_t currPosition;
mTrack->getPosition(&currPosition);
+ if (currPosition >= totalFrameCount) counter++;
+
if (testSeek && (currPosition > totalFrameCount * 0.6)) {
testSeek = false;
if (!mTrack->hasStarted()) return BAD_VALUE;
@@ -227,10 +227,9 @@
if (bufferPosition != setPosition) return BAD_VALUE;
mTrack->start();
}
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
- retry--;
+ std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_PERIOD_MS));
}
- if (!mStopPlaying) return TIMED_OUT;
+ if (!mStopPlaying && counter == maxTries) return TIMED_OUT;
return OK;
}
@@ -246,7 +245,7 @@
void AudioPlayback::stop() {
std::unique_lock<std::mutex> lock{mMutex};
mStopPlaying = true;
- if (mState != PLAY_STOPPED) {
+ if (mState != PLAY_STOPPED && mState != PLAY_NO_INIT) {
int32_t msec = 0;
(void)mTrack->pendingDuration(&msec);
mTrack->stopAndJoinCallbacks();
@@ -274,7 +273,7 @@
}
// no more frames to read
- if (mNumFramesReceived > mNumFramesToRecord || mStopRecording) {
+ if (mNumFramesReceived >= mNumFramesToRecord || mStopRecording) {
mStopRecording = true;
return 0;
}
@@ -369,14 +368,16 @@
AudioCapture::AudioCapture(audio_source_t inputSource, uint32_t sampleRate, audio_format_t format,
audio_channel_mask_t channelMask, audio_input_flags_t flags,
- audio_session_t sessionId, AudioRecord::transfer_type transferType)
+ audio_session_t sessionId, AudioRecord::transfer_type transferType,
+ const audio_attributes_t* attributes)
: mInputSource(inputSource),
mSampleRate(sampleRate),
mFormat(format),
mChannelMask(channelMask),
mFlags(flags),
mSessionId(sessionId),
- mTransferType(transferType) {
+ mTransferType(transferType),
+ mAttributes(attributes) {
mFrameCount = 0;
mNotificationFrames = 0;
mNumFramesToRecord = 0;
@@ -389,9 +390,6 @@
mReceivedCbMarkerCount = 0;
mState = REC_NO_INIT;
mStopRecording = false;
-#if RECORD_TO_FILE
- CreateRandomFile(mOutFileFd);
-#endif
}
AudioCapture::~AudioCapture() {
@@ -431,19 +429,20 @@
if (mSampleRate == 48000) { // test all available constructors
mRecord = new AudioRecord(mInputSource, mSampleRate, mFormat, mChannelMask,
attributionSource, mFrameCount, nullptr /* callback */,
- mNotificationFrames, mSessionId, mTransferType, mFlags);
+ mNotificationFrames, mSessionId, mTransferType, mFlags,
+ mAttributes);
} else {
mRecord = new AudioRecord(attributionSource);
status = mRecord->set(mInputSource, mSampleRate, mFormat, mChannelMask, mFrameCount,
nullptr /* callback */, 0 /* notificationFrames */,
false /* canCallJava */, mSessionId, mTransferType, mFlags,
- attributionSource.uid, attributionSource.pid);
+ attributionSource.uid, attributionSource.pid, mAttributes);
}
if (NO_ERROR != status) return status;
} else if (mTransferType == AudioRecord::TRANSFER_CALLBACK) {
mRecord = new AudioRecord(mInputSource, mSampleRate, mFormat, mChannelMask,
attributionSource, mFrameCount, this, mNotificationFrames,
- mSessionId, mTransferType, mFlags);
+ mSessionId, mTransferType, mFlags, mAttributes);
} else {
ALOGE("Test application is not handling transfer type %s",
AudioRecord::convertTransferToText(mTransferType));
@@ -460,6 +459,26 @@
return status;
}
+status_t AudioCapture::setRecordDuration(float durationInSec) {
+ if (REC_READY != mState) {
+ return INVALID_OPERATION;
+ }
+ uint32_t sampleRate = mSampleRate == 0 ? mRecord->getSampleRate() : mSampleRate;
+ mNumFramesToRecord = (sampleRate * durationInSec);
+ return OK;
+}
+
+status_t AudioCapture::enableRecordDump() {
+ if (mOutFileFd != -1) {
+ return INVALID_OPERATION;
+ }
+ TemporaryFile tf("/data/local/tmp");
+ tf.DoNotRemove();
+ mOutFileFd = tf.release();
+ mFileName = std::string{tf.path};
+ return OK;
+}
+
sp<AudioRecord> AudioCapture::getAudioRecordHandle() {
return (REC_NO_INIT == mState) ? nullptr : mRecord;
}
@@ -481,7 +500,7 @@
status_t AudioCapture::stop() {
status_t status = OK;
mStopRecording = true;
- if (mState != REC_STOPPED) {
+ if (mState != REC_STOPPED && mState != REC_NO_INIT) {
if (mInputSource != AUDIO_SOURCE_DEFAULT) {
bool state = false;
status = AudioSystem::isSourceActive(mInputSource, &state);
@@ -495,81 +514,66 @@
}
status_t AudioCapture::obtainBuffer(RawBuffer& buffer) {
- if (REC_STARTED != mState && REC_STOPPED != mState) return INVALID_OPERATION;
- int retry = 25;
- AudioRecord::Buffer recordBuffer;
- recordBuffer.frameCount = mNotificationFrames;
+ if (REC_STARTED != mState) return INVALID_OPERATION;
+ const int maxTries = MAX_WAIT_TIME_MS / WAIT_PERIOD_MS;
+ int counter = 0;
size_t nonContig = 0;
- status_t status = mRecord->obtainBuffer(&recordBuffer, retry, &nonContig);
- if (OK == status) {
- const int64_t timestampUs =
- ((1000000LL * mNumFramesReceived) + (mRecord->getSampleRate() >> 1)) /
- mRecord->getSampleRate();
- RawBuffer buff{-1, timestampUs, static_cast<int32_t>(recordBuffer.size())};
- memcpy(buff.mData.get(), recordBuffer.data(), recordBuffer.size());
- buffer = std::move(buff);
- mNumFramesReceived += recordBuffer.size() / mRecord->frameSize();
- mRecord->releaseBuffer(&recordBuffer);
- if (mNumFramesReceived > mNumFramesToRecord) {
- stop();
+ while (mNumFramesReceived < mNumFramesToRecord) {
+ AudioRecord::Buffer recordBuffer;
+ recordBuffer.frameCount = mNotificationFrames;
+ status_t status = mRecord->obtainBuffer(&recordBuffer, 1, &nonContig);
+ if (OK == status) {
+ const int64_t timestampUs =
+ ((1000000LL * mNumFramesReceived) + (mRecord->getSampleRate() >> 1)) /
+ mRecord->getSampleRate();
+ RawBuffer buff{-1, timestampUs, static_cast<int32_t>(recordBuffer.size())};
+ memcpy(buff.mData.get(), recordBuffer.data(), recordBuffer.size());
+ buffer = std::move(buff);
+ mNumFramesReceived += recordBuffer.size() / mRecord->frameSize();
+ mRecord->releaseBuffer(&recordBuffer);
+ counter = 0;
+ } else if (WOULD_BLOCK == status) {
+ // if not received a buffer for MAX_WAIT_TIME_MS, something has gone wrong
+ if (counter == maxTries) return TIMED_OUT;
+ counter++;
}
- } else if (status == WOULD_BLOCK) {
- if (mStopRecording)
- return WOULD_BLOCK;
- else
- return TIMED_OUT;
}
return OK;
}
status_t AudioCapture::obtainBufferCb(RawBuffer& buffer) {
if (REC_STARTED != mState) return INVALID_OPERATION;
- int retry = 10;
+ const int maxTries = MAX_WAIT_TIME_MS / WAIT_PERIOD_MS;
+ int counter = 0;
std::unique_lock<std::mutex> lock{mMutex};
- while (mBuffersReceived.empty() && !mStopRecording && retry > 0) {
- mCondition.wait_for(lock, std::chrono::milliseconds(100));
- retry--;
+ while (mBuffersReceived.empty() && !mStopRecording && counter < maxTries) {
+ mCondition.wait_for(lock, std::chrono::milliseconds(WAIT_PERIOD_MS));
+ counter++;
}
if (!mBuffersReceived.empty()) {
auto it = mBuffersReceived.begin();
buffer = std::move(*it);
mBuffersReceived.erase(it);
} else {
- if (retry == 0) return TIMED_OUT;
- if (mStopRecording)
- return WOULD_BLOCK;
- else
- return UNKNOWN_ERROR;
+ if (!mStopRecording && counter == maxTries) return TIMED_OUT;
}
return OK;
}
status_t AudioCapture::audioProcess() {
RawBuffer buffer;
- while (true) {
- status_t status;
+ status_t status = OK;
+ while (mNumFramesReceived < mNumFramesToRecord && status == OK) {
if (mTransferType == AudioRecord::TRANSFER_CALLBACK)
status = obtainBufferCb(buffer);
else
status = obtainBuffer(buffer);
- switch (status) {
- case OK:
- if (mOutFileFd > 0) {
- const char* ptr =
- static_cast<const char*>(static_cast<void*>(buffer.mData.get()));
- write(mOutFileFd, ptr, buffer.mCapacity);
- }
- break;
- case WOULD_BLOCK:
- return OK;
- case TIMED_OUT: // "recorder application timed out from receiving buffers"
- case NO_INIT: // "recorder not initialized"
- case INVALID_OPERATION: // "recorder not started"
- case UNKNOWN_ERROR: // "Unknown error"
- default:
- return status;
+ if (OK == status && mOutFileFd > 0) {
+ const char* ptr = static_cast<const char*>(static_cast<void*>(buffer.mData.get()));
+ write(mOutFileFd, ptr, buffer.mCapacity);
}
}
+ return OK;
}
status_t listAudioPorts(std::vector<audio_port_v7>& portsVec) {
@@ -613,13 +617,15 @@
}
status_t getPortByAttributes(audio_port_role_t role, audio_port_type_t type,
- audio_devices_t deviceType, audio_port_v7& port) {
+ audio_devices_t deviceType, const std::string& address,
+ audio_port_v7& port) {
std::vector<struct audio_port_v7> ports;
status_t status = listAudioPorts(ports);
if (status != OK) return status;
for (auto i = 0; i < ports.size(); i++) {
if (ports[i].role == role && ports[i].type == type &&
- ports[i].ext.device.type == deviceType) {
+ ports[i].ext.device.type == deviceType &&
+ !strncmp(ports[i].ext.device.address, address.c_str(), AUDIO_DEVICE_MAX_ADDRESS_LEN)) {
port = ports[i];
return OK;
}
diff --git a/media/libaudioclient/tests/audio_test_utils.h b/media/libaudioclient/tests/audio_test_utils.h
index f35b65d..90c30c2 100644
--- a/media/libaudioclient/tests/audio_test_utils.h
+++ b/media/libaudioclient/tests/audio_test_utils.h
@@ -34,8 +34,6 @@
#include <media/AudioRecord.h>
#include <media/AudioTrack.h>
-#define RECORD_TO_FILE 0
-
using namespace android;
struct MixPort {
@@ -53,11 +51,11 @@
status_t parse_audio_policy_configuration_xml(std::vector<std::string>& attachedDevices,
std::vector<MixPort>& mixPorts,
std::vector<Route>& routes);
-void CreateRandomFile(int& fd);
status_t listAudioPorts(std::vector<audio_port_v7>& portsVec);
status_t listAudioPatches(std::vector<struct audio_patch>& patchesVec);
status_t getPortByAttributes(audio_port_role_t role, audio_port_type_t type,
- audio_devices_t deviceType, audio_port_v7& port);
+ audio_devices_t deviceType, const std::string& address,
+ audio_port_v7& port);
status_t getPatchForOutputMix(audio_io_handle_t audioIo, audio_patch& patch);
status_t getPatchForInputMix(audio_io_handle_t audioIo, audio_patch& patch);
bool patchContainsOutputDevice(audio_port_handle_t deviceId, audio_patch patch);
@@ -76,7 +74,7 @@
std::condition_variable mCondition;
void onAudioDeviceUpdate(audio_io_handle_t audioIo, audio_port_handle_t deviceId);
- status_t waitForAudioDeviceCb();
+ status_t waitForAudioDeviceCb(audio_port_handle_t expDeviceId = AUDIO_PORT_HANDLE_NONE);
};
// Simple AudioPlayback class.
@@ -148,7 +146,8 @@
audio_channel_mask_t channelMask,
audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE,
audio_session_t sessionId = AUDIO_SESSION_ALLOCATE,
- AudioRecord::transfer_type transferType = AudioRecord::TRANSFER_CALLBACK);
+ AudioRecord::transfer_type transferType = AudioRecord::TRANSFER_CALLBACK,
+ const audio_attributes_t* attributes = nullptr);
~AudioCapture();
size_t onMoreData(const AudioRecord::Buffer& buffer) override;
void onOverrun() override;
@@ -156,6 +155,9 @@
void onNewPos(uint32_t newPos) override;
void onNewIAudioRecord() override;
status_t create();
+ status_t setRecordDuration(float durationInSec);
+ status_t enableRecordDump();
+ std::string getRecordDumpFileName() const { return mFileName; }
sp<AudioRecord> getAudioRecordHandle();
status_t start(AudioSystem::sync_event_t event = AudioSystem::SYNC_EVENT_NONE,
audio_session_t triggerSession = AUDIO_SESSION_NONE);
@@ -190,11 +192,13 @@
const audio_input_flags_t mFlags;
const audio_session_t mSessionId;
const AudioRecord::transfer_type mTransferType;
+ const audio_attributes_t* mAttributes;
size_t mMaxBytesPerCallback = 2048;
sp<AudioRecord> mRecord;
State mState;
bool mStopRecording;
+ std::string mFileName;
int mOutFileFd = -1;
std::mutex mMutex;
diff --git a/media/libaudioclient/tests/audioeffect_analyser.cpp b/media/libaudioclient/tests/audioeffect_analyser.cpp
new file mode 100644
index 0000000..94accae
--- /dev/null
+++ b/media/libaudioclient/tests/audioeffect_analyser.cpp
@@ -0,0 +1,419 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "AudioEffectAnalyser"
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+#include <media/AudioEffect.h>
+#include <system/audio_effects/effect_bassboost.h>
+#include <system/audio_effects/effect_equalizer.h>
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include "audio_test_utils.h"
+#include "pffft.hpp"
+
+#define CHECK_OK(expr, msg) \
+ mStatus = (expr); \
+ if (OK != mStatus) { \
+ mMsg = (msg); \
+ return; \
+ }
+
+using namespace android;
+
+constexpr float kDefAmplitude = 0.60f;
+
+constexpr float kPlayBackDurationSec = 1.5;
+constexpr float kCaptureDurationSec = 1.0;
+constexpr float kPrimeDurationInSec = 0.5;
+
+// chosen to safely sample largest center freq of eq bands
+constexpr uint32_t kSamplingFrequency = 48000;
+
+// allows no fmt conversion before fft
+constexpr audio_format_t kFormat = AUDIO_FORMAT_PCM_FLOAT;
+
+// playback and capture are done with channel mask configured to mono.
+// effect analysis should not depend on mask, mono makes it easier.
+
+constexpr int kNPointFFT = 16384;
+constexpr float kBinWidth = (float)kSamplingFrequency / kNPointFFT;
+
+const char* gPackageName = "AudioEffectAnalyser";
+
+static_assert(kPrimeDurationInSec + 2 * kNPointFFT / kSamplingFrequency < kCaptureDurationSec,
+ "capture at least, prime, pad, nPointFft size of samples");
+static_assert(kPrimeDurationInSec + 2 * kNPointFFT / kSamplingFrequency < kPlayBackDurationSec,
+ "playback needs to be active during capture");
+
+struct CaptureEnv {
+ // input args
+ uint32_t mSampleRate{kSamplingFrequency};
+ audio_format_t mFormat{kFormat};
+ audio_channel_mask_t mChannelMask{AUDIO_CHANNEL_IN_MONO};
+ float mCaptureDuration{kCaptureDurationSec};
+ // output val
+ status_t mStatus{OK};
+ std::string mMsg;
+ std::string mDumpFileName;
+
+ ~CaptureEnv();
+ void capture();
+};
+
+CaptureEnv::~CaptureEnv() {
+ if (!mDumpFileName.empty()) {
+ std::ifstream f(mDumpFileName);
+ if (f.good()) {
+ f.close();
+ remove(mDumpFileName.c_str());
+ }
+ }
+}
+
+void CaptureEnv::capture() {
+ audio_port_v7 port;
+ CHECK_OK(getPortByAttributes(AUDIO_PORT_ROLE_SOURCE, AUDIO_PORT_TYPE_DEVICE,
+ AUDIO_DEVICE_IN_REMOTE_SUBMIX, "0", port),
+ "Could not find port")
+ const auto capture =
+ sp<AudioCapture>::make(AUDIO_SOURCE_REMOTE_SUBMIX, mSampleRate, mFormat, mChannelMask);
+ CHECK_OK(capture->create(), "record creation failed")
+ CHECK_OK(capture->setRecordDuration(mCaptureDuration), "set record duration failed")
+ CHECK_OK(capture->enableRecordDump(), "enable record dump failed")
+ auto cbCapture = sp<OnAudioDeviceUpdateNotifier>::make();
+ CHECK_OK(capture->getAudioRecordHandle()->addAudioDeviceCallback(cbCapture),
+ "addAudioDeviceCallback failed")
+ CHECK_OK(capture->start(), "start recording failed")
+ CHECK_OK(capture->audioProcess(), "recording process failed")
+ CHECK_OK(cbCapture->waitForAudioDeviceCb(), "audio device callback notification timed out");
+ if (port.id != capture->getAudioRecordHandle()->getRoutedDeviceId()) {
+ CHECK_OK(BAD_VALUE, "Capture NOT routed on expected port")
+ }
+ CHECK_OK(getPortByAttributes(AUDIO_PORT_ROLE_SINK, AUDIO_PORT_TYPE_DEVICE,
+ AUDIO_DEVICE_OUT_REMOTE_SUBMIX, "0", port),
+ "Could not find port")
+ CHECK_OK(capture->stop(), "record stop failed")
+ mDumpFileName = capture->getRecordDumpFileName();
+}
+
+struct PlaybackEnv {
+ // input args
+ uint32_t mSampleRate{kSamplingFrequency};
+ audio_format_t mFormat{kFormat};
+ audio_channel_mask_t mChannelMask{AUDIO_CHANNEL_OUT_MONO};
+ audio_session_t mSessionId{AUDIO_SESSION_NONE};
+ std::string mRes;
+ // output val
+ status_t mStatus{OK};
+ std::string mMsg;
+
+ void play();
+};
+
+void PlaybackEnv::play() {
+ const auto ap =
+ sp<AudioPlayback>::make(mSampleRate, mFormat, mChannelMask, AUDIO_OUTPUT_FLAG_NONE,
+ mSessionId, AudioTrack::TRANSFER_OBTAIN);
+ CHECK_OK(ap->loadResource(mRes.c_str()), "Unable to open Resource")
+ const auto cbPlayback = sp<OnAudioDeviceUpdateNotifier>::make();
+ CHECK_OK(ap->create(), "track creation failed")
+ ap->getAudioTrackHandle()->setVolume(1.0f);
+ CHECK_OK(ap->getAudioTrackHandle()->addAudioDeviceCallback(cbPlayback),
+ "addAudioDeviceCallback failed")
+ CHECK_OK(ap->start(), "audio track start failed")
+ CHECK_OK(cbPlayback->waitForAudioDeviceCb(), "audio device callback notification timed out")
+ CHECK_OK(ap->onProcess(), "playback process failed")
+ ap->stop();
+}
+
+void generateMultiTone(const std::vector<int>& toneFrequencies, float samplingFrequency,
+ float duration, float amplitude, float* buffer, int numSamples) {
+ int totalFrameCount = (samplingFrequency * duration);
+ int limit = std::min(totalFrameCount, numSamples);
+
+ for (auto i = 0; i < limit; i++) {
+ buffer[i] = 0;
+ for (auto j = 0; j < toneFrequencies.size(); j++) {
+ buffer[i] += sin(2 * M_PI * toneFrequencies[j] * i / samplingFrequency);
+ }
+ buffer[i] *= (amplitude / toneFrequencies.size());
+ }
+}
+
+sp<AudioEffect> createEffect(const effect_uuid_t* type,
+ audio_session_t sessionId = AUDIO_SESSION_OUTPUT_MIX) {
+ std::string packageName{gPackageName};
+ AttributionSourceState attributionSource;
+ attributionSource.packageName = packageName;
+ attributionSource.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(getuid()));
+ attributionSource.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(getpid()));
+ attributionSource.token = sp<BBinder>::make();
+ sp<AudioEffect> effect = sp<AudioEffect>::make(attributionSource);
+ effect->set(type, nullptr, 0, nullptr, sessionId, AUDIO_IO_HANDLE_NONE, {}, false, false);
+ return effect;
+}
+
+void computeFilterGainsAtTones(float captureDuration, int nPointFft, std::vector<int>& binOffsets,
+ float* inputMag, float* gaindB, const char* res,
+ audio_session_t sessionId) {
+ int totalFrameCount = captureDuration * kSamplingFrequency;
+ auto output = pffft::AlignedVector<float>(totalFrameCount);
+ auto fftOutput = pffft::AlignedVector<float>(nPointFft);
+ PlaybackEnv argsP;
+ argsP.mRes = std::string{res};
+ argsP.mSessionId = sessionId;
+ CaptureEnv argsR;
+ argsR.mCaptureDuration = captureDuration;
+ std::thread playbackThread(&PlaybackEnv::play, &argsP);
+ std::thread captureThread(&CaptureEnv::capture, &argsR);
+ captureThread.join();
+ playbackThread.join();
+ ASSERT_EQ(OK, argsR.mStatus) << argsR.mMsg;
+ ASSERT_EQ(OK, argsP.mStatus) << argsP.mMsg;
+ ASSERT_FALSE(argsR.mDumpFileName.empty()) << "recorded not written to file";
+ std::ifstream fin(argsR.mDumpFileName, std::ios::in | std::ios::binary);
+ fin.read((char*)output.data(), totalFrameCount * sizeof(output[0]));
+ fin.close();
+ PFFFT_Setup* handle = pffft_new_setup(nPointFft, PFFFT_REAL);
+ // ignore first few samples. This is to not analyse until audio track is re-routed to remote
+ // submix source, also for the effect filter response to reach steady-state (priming / pruning
+ // samples).
+ int rerouteOffset = kPrimeDurationInSec * kSamplingFrequency;
+ pffft_transform_ordered(handle, output.data() + rerouteOffset, fftOutput.data(), nullptr,
+ PFFFT_FORWARD);
+ pffft_destroy_setup(handle);
+ for (auto i = 0; i < binOffsets.size(); i++) {
+ auto k = binOffsets[i];
+ auto outputMag = sqrt((fftOutput[k * 2] * fftOutput[k * 2]) +
+ (fftOutput[k * 2 + 1] * fftOutput[k * 2 + 1]));
+ gaindB[i] = 20 * log10(outputMag / inputMag[i]);
+ }
+}
+
+std::tuple<int, int> roundToFreqCenteredToFftBin(float binWidth, float freq) {
+ int bin_index = std::round(freq / binWidth);
+ int cfreq = std::round(bin_index * binWidth);
+ return std::make_tuple(bin_index, cfreq);
+}
+
+TEST(AudioEffectTest, CheckEqualizerEffect) {
+ audio_session_t sessionId =
+ (audio_session_t)AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
+ sp<AudioEffect> equalizer = createEffect(SL_IID_EQUALIZER, sessionId);
+ ASSERT_EQ(OK, equalizer->initCheck());
+ ASSERT_EQ(NO_ERROR, equalizer->setEnabled(true));
+ if ((equalizer->descriptor().flags & EFFECT_FLAG_HW_ACC_MASK) != 0) {
+ GTEST_SKIP() << "effect processed output inaccessible, skipping test";
+ }
+#define MAX_PARAMS 64
+ uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + MAX_PARAMS];
+ effect_param_t* eqParam = (effect_param_t*)(&buf32);
+
+ // get num of presets
+ eqParam->psize = sizeof(uint32_t);
+ eqParam->vsize = sizeof(uint16_t);
+ *(int32_t*)eqParam->data = EQ_PARAM_GET_NUM_OF_PRESETS;
+ EXPECT_EQ(0, equalizer->getParameter(eqParam));
+ EXPECT_EQ(0, eqParam->status);
+ int numPresets = *((uint16_t*)((int32_t*)eqParam->data + 1));
+
+ // get num of bands
+ eqParam->psize = sizeof(uint32_t);
+ eqParam->vsize = sizeof(uint16_t);
+ *(int32_t*)eqParam->data = EQ_PARAM_NUM_BANDS;
+ EXPECT_EQ(0, equalizer->getParameter(eqParam));
+ EXPECT_EQ(0, eqParam->status);
+ int numBands = *((uint16_t*)((int32_t*)eqParam->data + 1));
+
+ const int totalFrameCount = kSamplingFrequency * kPlayBackDurationSec;
+
+ // get band center frequencies
+ std::vector<int> centerFrequencies;
+ std::vector<int> binOffsets;
+ for (auto i = 0; i < numBands; i++) {
+ eqParam->psize = sizeof(uint32_t) * 2;
+ eqParam->vsize = sizeof(uint32_t);
+ *(int32_t*)eqParam->data = EQ_PARAM_CENTER_FREQ;
+ *((uint16_t*)((int32_t*)eqParam->data + 1)) = i;
+ EXPECT_EQ(0, equalizer->getParameter(eqParam));
+ EXPECT_EQ(0, eqParam->status);
+ float cfreq = *((int32_t*)eqParam->data + 2) / 1000; // milli hz
+ // pick frequency close to bin center frequency
+ auto [bin_index, bin_freq] = roundToFreqCenteredToFftBin(kBinWidth, cfreq);
+ centerFrequencies.push_back(bin_freq);
+ binOffsets.push_back(bin_index);
+ }
+
+ // input for effect module
+ auto input = pffft::AlignedVector<float>(totalFrameCount);
+ generateMultiTone(centerFrequencies, kSamplingFrequency, kPlayBackDurationSec, kDefAmplitude,
+ input.data(), totalFrameCount);
+ auto fftInput = pffft::AlignedVector<float>(kNPointFFT);
+ PFFFT_Setup* handle = pffft_new_setup(kNPointFFT, PFFFT_REAL);
+ pffft_transform_ordered(handle, input.data(), fftInput.data(), nullptr, PFFFT_FORWARD);
+ pffft_destroy_setup(handle);
+ float inputMag[numBands];
+ for (auto i = 0; i < numBands; i++) {
+ auto k = binOffsets[i];
+ inputMag[i] = sqrt((fftInput[k * 2] * fftInput[k * 2]) +
+ (fftInput[k * 2 + 1] * fftInput[k * 2 + 1]));
+ }
+ TemporaryFile tf("/data/local/tmp");
+ close(tf.release());
+ std::ofstream fout(tf.path, std::ios::out | std::ios::binary);
+ fout.write((char*)input.data(), input.size() * sizeof(input[0]));
+ fout.close();
+
+ float expGaindB[numBands], actGaindB[numBands];
+
+ std::string msg = "";
+ int numPresetsOk = 0;
+ for (auto preset = 0; preset < numPresets; preset++) {
+ // set preset
+ eqParam->psize = sizeof(uint32_t);
+ eqParam->vsize = sizeof(uint32_t);
+ *(int32_t*)eqParam->data = EQ_PARAM_CUR_PRESET;
+ *((uint16_t*)((int32_t*)eqParam->data + 1)) = preset;
+ EXPECT_EQ(0, equalizer->setParameter(eqParam));
+ EXPECT_EQ(0, eqParam->status);
+ // get preset gains
+ eqParam->psize = sizeof(uint32_t);
+ eqParam->vsize = (numBands + 1) * sizeof(uint32_t);
+ *(int32_t*)eqParam->data = EQ_PARAM_PROPERTIES;
+ EXPECT_EQ(0, equalizer->getParameter(eqParam));
+ EXPECT_EQ(0, eqParam->status);
+ t_equalizer_settings* settings =
+ reinterpret_cast<t_equalizer_settings*>((int32_t*)eqParam->data + 1);
+ EXPECT_EQ(preset, settings->curPreset);
+ EXPECT_EQ(numBands, settings->numBands);
+ for (auto i = 0; i < numBands; i++) {
+ expGaindB[i] = ((int16_t)settings->bandLevels[i]) / 100.0f; // gain in milli bels
+ }
+ memset(actGaindB, 0, sizeof(actGaindB));
+ ASSERT_NO_FATAL_FAILURE(computeFilterGainsAtTones(kCaptureDurationSec, kNPointFFT,
+ binOffsets, inputMag, actGaindB, tf.path,
+ sessionId));
+ bool isOk = true;
+ for (auto i = 0; i < numBands - 1; i++) {
+ auto diffA = expGaindB[i] - expGaindB[i + 1];
+ auto diffB = actGaindB[i] - actGaindB[i + 1];
+ if (diffA == 0 && fabs(diffA - diffB) > 1.0f) {
+ msg += (android::base::StringPrintf(
+ "For eq preset : %d, between bands %d and %d, expected relative gain is : "
+ "%f, got relative gain is : %f, error : %f \n",
+ preset, i, i + 1, diffA, diffB, diffA - diffB));
+ isOk = false;
+ } else if (diffA * diffB < 0) {
+ msg += (android::base::StringPrintf(
+ "For eq preset : %d, between bands %d and %d, expected relative gain and "
+ "seen relative gain are of opposite signs \n. Expected relative gain is : "
+ "%f, seen relative gain is : %f \n",
+ preset, i, i + 1, diffA, diffB));
+ isOk = false;
+ }
+ }
+ if (isOk) numPresetsOk++;
+ }
+ EXPECT_EQ(numPresetsOk, numPresets) << msg;
+}
+
+TEST(AudioEffectTest, CheckBassBoostEffect) {
+ audio_session_t sessionId =
+ (audio_session_t)AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
+ sp<AudioEffect> bassboost = createEffect(SL_IID_BASSBOOST, sessionId);
+ ASSERT_EQ(OK, bassboost->initCheck());
+ ASSERT_EQ(NO_ERROR, bassboost->setEnabled(true));
+ if ((bassboost->descriptor().flags & EFFECT_FLAG_HW_ACC_MASK) != 0) {
+ GTEST_SKIP() << "effect processed output inaccessible, skipping test";
+ }
+ int32_t buf32[sizeof(effect_param_t) / sizeof(int32_t) + MAX_PARAMS];
+ effect_param_t* bbParam = (effect_param_t*)(&buf32);
+
+ bbParam->psize = sizeof(int32_t);
+ bbParam->vsize = sizeof(int32_t);
+ *(int32_t*)bbParam->data = BASSBOOST_PARAM_STRENGTH_SUPPORTED;
+ EXPECT_EQ(0, bassboost->getParameter(bbParam));
+ EXPECT_EQ(0, bbParam->status);
+ bool strengthSupported = *((int32_t*)bbParam->data + 1);
+
+ const int totalFrameCount = kSamplingFrequency * kPlayBackDurationSec;
+
+ // selecting bass frequency, speech tone (for relative gain)
+ std::vector<int> testFrequencies{100, 1200};
+ std::vector<int> binOffsets;
+ for (auto i = 0; i < testFrequencies.size(); i++) {
+ // pick frequency close to bin center frequency
+ auto [bin_index, bin_freq] = roundToFreqCenteredToFftBin(kBinWidth, testFrequencies[i]);
+ testFrequencies[i] = bin_freq;
+ binOffsets.push_back(bin_index);
+ }
+
+ // input for effect module
+ auto input = pffft::AlignedVector<float>(totalFrameCount);
+ generateMultiTone(testFrequencies, kSamplingFrequency, kPlayBackDurationSec, kDefAmplitude,
+ input.data(), totalFrameCount);
+ auto fftInput = pffft::AlignedVector<float>(kNPointFFT);
+ PFFFT_Setup* handle = pffft_new_setup(kNPointFFT, PFFFT_REAL);
+ pffft_transform_ordered(handle, input.data(), fftInput.data(), nullptr, PFFFT_FORWARD);
+ pffft_destroy_setup(handle);
+ float inputMag[testFrequencies.size()];
+ for (auto i = 0; i < testFrequencies.size(); i++) {
+ auto k = binOffsets[i];
+ inputMag[i] = sqrt((fftInput[k * 2] * fftInput[k * 2]) +
+ (fftInput[k * 2 + 1] * fftInput[k * 2 + 1]));
+ }
+ TemporaryFile tf("/data/local/tmp");
+ close(tf.release());
+ std::ofstream fout(tf.path, std::ios::out | std::ios::binary);
+ fout.write((char*)input.data(), input.size() * sizeof(input[0]));
+ fout.close();
+
+ float gainWithOutFilter[testFrequencies.size()];
+ memset(gainWithOutFilter, 0, sizeof(gainWithOutFilter));
+ ASSERT_NO_FATAL_FAILURE(computeFilterGainsAtTones(kCaptureDurationSec, kNPointFFT, binOffsets,
+ inputMag, gainWithOutFilter, tf.path,
+ AUDIO_SESSION_OUTPUT_MIX));
+ float diffA = gainWithOutFilter[0] - gainWithOutFilter[1];
+ float prevGain = -100.f;
+ for (auto strength = 150; strength < 1000; strength += strengthSupported ? 150 : 1000) {
+ // configure filter strength
+ if (strengthSupported) {
+ bbParam->psize = sizeof(int32_t);
+ bbParam->vsize = sizeof(int16_t);
+ *(int32_t*)bbParam->data = BASSBOOST_PARAM_STRENGTH;
+ *((int16_t*)((int32_t*)bbParam->data + 1)) = strength;
+ EXPECT_EQ(0, bassboost->setParameter(bbParam));
+ EXPECT_EQ(0, bbParam->status);
+ }
+ float gainWithFilter[testFrequencies.size()];
+ memset(gainWithFilter, 0, sizeof(gainWithFilter));
+ ASSERT_NO_FATAL_FAILURE(computeFilterGainsAtTones(kCaptureDurationSec, kNPointFFT,
+ binOffsets, inputMag, gainWithFilter,
+ tf.path, sessionId));
+ float diffB = gainWithFilter[0] - gainWithFilter[1];
+ EXPECT_GT(diffB, diffA) << "bassboost effect not seen";
+ EXPECT_GE(diffB, prevGain) << "increase in boost strength causing fall in gain";
+ prevGain = diffB;
+ }
+}
diff --git a/media/libaudioclient/tests/audiorouting_tests.cpp b/media/libaudioclient/tests/audiorouting_tests.cpp
index 4bd81c8..2c5fcd7 100644
--- a/media/libaudioclient/tests/audiorouting_tests.cpp
+++ b/media/libaudioclient/tests/audiorouting_tests.cpp
@@ -46,10 +46,10 @@
attributes.usage = AUDIO_USAGE_MEDIA;
attributes.content_type = AUDIO_CONTENT_TYPE_MUSIC;
attributes.flags = flags[i];
- sp<AudioPlayback> ap = sp<AudioPlayback>::make(
- 0 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
- AUDIO_OUTPUT_FLAG_NONE, AUDIO_SESSION_NONE, AudioTrack::TRANSFER_OBTAIN,
- &attributes);
+ sp<AudioPlayback> ap = sp<AudioPlayback>::make(0 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT,
+ AUDIO_CHANNEL_OUT_STEREO,
+ AUDIO_OUTPUT_FLAG_NONE, AUDIO_SESSION_NONE,
+ AudioTrack::TRANSFER_OBTAIN, &attributes);
ASSERT_NE(nullptr, ap);
ASSERT_EQ(OK, ap->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"))
<< "Unable to open Resource";
@@ -77,46 +77,186 @@
}
}
-TEST(AudioTrackTest, TestRemoteSubmix) {
- std::vector<std::string> attachedDevices;
- std::vector<MixPort> mixPorts;
- std::vector<Route> routes;
- EXPECT_EQ(OK, parse_audio_policy_configuration_xml(attachedDevices, mixPorts, routes));
- bool hasFlag = false;
- for (int j = 0; j < attachedDevices.size() && !hasFlag; j++) {
- if (attachedDevices[j].find("Remote Submix") != -1) hasFlag = true;
+TEST(AudioTrackTest, DefaultRoutingTest) {
+ audio_port_v7 port;
+ if (OK != getPortByAttributes(AUDIO_PORT_ROLE_SOURCE, AUDIO_PORT_TYPE_DEVICE,
+ AUDIO_DEVICE_IN_REMOTE_SUBMIX, "0", port)) {
+ GTEST_SKIP() << "remote submix in device not connected";
}
- if (!hasFlag) GTEST_SKIP() << " Device does not have Remote Submix port.";
- sp<AudioCapture> capture = new AudioCapture(AUDIO_SOURCE_REMOTE_SUBMIX, 48000,
- AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO);
- ASSERT_NE(nullptr, capture);
- ASSERT_EQ(OK, capture->create()) << "record creation failed";
+ // create record instance
+ sp<AudioCapture> capture = sp<AudioCapture>::make(
+ AUDIO_SOURCE_REMOTE_SUBMIX, 48000, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO);
+ ASSERT_NE(nullptr, capture);
+ EXPECT_EQ(OK, capture->create()) << "record creation failed";
+ sp<OnAudioDeviceUpdateNotifier> cbCapture = sp<OnAudioDeviceUpdateNotifier>::make();
+ EXPECT_EQ(OK, capture->getAudioRecordHandle()->addAudioDeviceCallback(cbCapture));
+
+ // create playback instance
sp<AudioPlayback> playback = sp<AudioPlayback>::make(
48000 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
AUDIO_OUTPUT_FLAG_NONE, AUDIO_SESSION_NONE);
ASSERT_NE(nullptr, playback);
ASSERT_EQ(OK, playback->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"))
<< "Unable to open Resource";
- ASSERT_EQ(OK, playback->create()) << "track creation failed";
+ EXPECT_EQ(OK, playback->create()) << "track creation failed";
+ sp<OnAudioDeviceUpdateNotifier> cbPlayback = sp<OnAudioDeviceUpdateNotifier>::make();
+ EXPECT_EQ(OK, playback->getAudioTrackHandle()->addAudioDeviceCallback(cbPlayback));
- audio_port_v7 port;
- status_t status = getPortByAttributes(AUDIO_PORT_ROLE_SOURCE, AUDIO_PORT_TYPE_DEVICE,
- AUDIO_DEVICE_IN_REMOTE_SUBMIX, port);
- EXPECT_EQ(OK, status) << "Could not find port";
-
+ // capture should be routed to submix in port
EXPECT_EQ(OK, capture->start()) << "start recording failed";
+ EXPECT_EQ(OK, cbCapture->waitForAudioDeviceCb());
EXPECT_EQ(port.id, capture->getAudioRecordHandle()->getRoutedDeviceId())
<< "Capture NOT routed on expected port";
- status = getPortByAttributes(AUDIO_PORT_ROLE_SINK, AUDIO_PORT_TYPE_DEVICE,
- AUDIO_DEVICE_OUT_REMOTE_SUBMIX, port);
+ // capture start should create submix out port
+ status_t status = getPortByAttributes(AUDIO_PORT_ROLE_SINK, AUDIO_PORT_TYPE_DEVICE,
+ AUDIO_DEVICE_OUT_REMOTE_SUBMIX, "0", port);
EXPECT_EQ(OK, status) << "Could not find port";
+ // playback should be routed to submix out as long as capture is active
EXPECT_EQ(OK, playback->start()) << "audio track start failed";
- EXPECT_EQ(OK, playback->onProcess());
- ASSERT_EQ(port.id, playback->getAudioTrackHandle()->getRoutedDeviceId())
+ EXPECT_EQ(OK, cbPlayback->waitForAudioDeviceCb());
+ EXPECT_EQ(port.id, playback->getAudioTrackHandle()->getRoutedDeviceId())
<< "Playback NOT routed on expected port";
+
capture->stop();
playback->stop();
}
+
+class AudioRoutingTest : public ::testing::Test {
+ public:
+ void SetUp() override {
+ audio_port_v7 port;
+ if (OK != getPortByAttributes(AUDIO_PORT_ROLE_SOURCE, AUDIO_PORT_TYPE_DEVICE,
+ AUDIO_DEVICE_IN_REMOTE_SUBMIX, "0", port)) {
+ GTEST_SKIP() << "remote submix in device not connected";
+ }
+ uint32_t mixType = MIX_TYPE_PLAYERS;
+ uint32_t mixFlag = MIX_ROUTE_FLAG_LOOP_BACK;
+ audio_devices_t deviceType = AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
+ AudioMixMatchCriterion criterion(AUDIO_USAGE_MEDIA, AUDIO_SOURCE_DEFAULT,
+ RULE_MATCH_ATTRIBUTE_USAGE);
+ std::vector<AudioMixMatchCriterion> criteria{criterion};
+ audio_config_t config = AUDIO_CONFIG_INITIALIZER;
+ config.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+ config.format = AUDIO_FORMAT_PCM_16_BIT;
+ config.sample_rate = 48000;
+ AudioMix mix(criteria, mixType, config, mixFlag, String8{mAddress.c_str()}, 0);
+ mix.mDeviceType = deviceType;
+ mMixes.push(mix);
+ if (OK == AudioSystem::registerPolicyMixes(mMixes, true)) {
+ mPolicyMixRegistered = true;
+ }
+ ASSERT_TRUE(mPolicyMixRegistered) << "register policy mix failed";
+ }
+
+ void TearDown() override {
+ if (mPolicyMixRegistered) {
+ EXPECT_EQ(OK, AudioSystem::registerPolicyMixes(mMixes, false));
+ }
+ }
+
+ bool mPolicyMixRegistered{false};
+ std::string mAddress{"mix_1"};
+ Vector<AudioMix> mMixes;
+};
+
+TEST_F(AudioRoutingTest, ConcurrentDynamicRoutingTest) {
+ audio_port_v7 port, port_mix;
+ // expect legacy submix in port to be connected
+ status_t status = getPortByAttributes(AUDIO_PORT_ROLE_SOURCE, AUDIO_PORT_TYPE_DEVICE,
+ AUDIO_DEVICE_IN_REMOTE_SUBMIX, "0", port);
+ EXPECT_EQ(OK, status) << "Could not find port";
+
+ // as policy mix is registered, expect submix in port with mAddress to be connected
+ status = getPortByAttributes(AUDIO_PORT_ROLE_SOURCE, AUDIO_PORT_TYPE_DEVICE,
+ AUDIO_DEVICE_IN_REMOTE_SUBMIX, mAddress, port_mix);
+ EXPECT_EQ(OK, status) << "Could not find port";
+
+ // create playback instance
+ sp<AudioPlayback> playback = sp<AudioPlayback>::make(
+ 48000 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
+ AUDIO_OUTPUT_FLAG_NONE, AUDIO_SESSION_NONE, AudioTrack::TRANSFER_OBTAIN);
+ ASSERT_NE(nullptr, playback);
+ ASSERT_EQ(OK, playback->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"))
+ << "Unable to open Resource";
+ EXPECT_EQ(OK, playback->create()) << "track creation failed";
+ sp<OnAudioDeviceUpdateNotifier> cbPlayback = sp<OnAudioDeviceUpdateNotifier>::make();
+ EXPECT_EQ(OK, playback->getAudioTrackHandle()->addAudioDeviceCallback(cbPlayback));
+
+ // create capture instances on different ports
+ sp<AudioCapture> captureA = sp<AudioCapture>::make(
+ AUDIO_SOURCE_REMOTE_SUBMIX, 48000, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO);
+ ASSERT_NE(nullptr, captureA);
+ EXPECT_EQ(OK, captureA->create()) << "record creation failed";
+ sp<OnAudioDeviceUpdateNotifier> cbCaptureA = sp<OnAudioDeviceUpdateNotifier>::make();
+ EXPECT_EQ(OK, captureA->getAudioRecordHandle()->addAudioDeviceCallback(cbCaptureA));
+
+ audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER;
+ attr.source = AUDIO_SOURCE_REMOTE_SUBMIX;
+ sprintf(attr.tags, "addr=%s", mAddress.c_str());
+ sp<AudioCapture> captureB = sp<AudioCapture>::make(
+ AUDIO_SOURCE_REMOTE_SUBMIX, 48000, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
+ AUDIO_INPUT_FLAG_NONE, AUDIO_SESSION_ALLOCATE, AudioRecord::TRANSFER_CALLBACK, &attr);
+ ASSERT_NE(nullptr, captureB);
+ EXPECT_EQ(OK, captureB->create()) << "record creation failed";
+ sp<OnAudioDeviceUpdateNotifier> cbCaptureB = sp<OnAudioDeviceUpdateNotifier>::make();
+ EXPECT_EQ(OK, captureB->getAudioRecordHandle()->addAudioDeviceCallback(cbCaptureB));
+
+ // launch
+ EXPECT_EQ(OK, captureA->start()) << "start recording failed";
+ EXPECT_EQ(OK, cbCaptureA->waitForAudioDeviceCb());
+ EXPECT_EQ(port.id, captureA->getAudioRecordHandle()->getRoutedDeviceId())
+ << "Capture NOT routed on expected port";
+
+ EXPECT_EQ(OK, captureB->start()) << "start recording failed";
+ EXPECT_EQ(OK, cbCaptureB->waitForAudioDeviceCb());
+ EXPECT_EQ(port_mix.id, captureB->getAudioRecordHandle()->getRoutedDeviceId())
+ << "Capture NOT routed on expected port";
+
+ // as record started, expect submix out ports to be connected
+ status = getPortByAttributes(AUDIO_PORT_ROLE_SINK, AUDIO_PORT_TYPE_DEVICE,
+ AUDIO_DEVICE_OUT_REMOTE_SUBMIX, "0", port);
+ EXPECT_EQ(OK, status) << "unexpected submix out port found";
+
+ status = getPortByAttributes(AUDIO_PORT_ROLE_SINK, AUDIO_PORT_TYPE_DEVICE,
+ AUDIO_DEVICE_OUT_REMOTE_SUBMIX, mAddress, port_mix);
+ EXPECT_EQ(OK, status) << "Could not find port";
+
+ // check if playback routed to desired port
+ EXPECT_EQ(OK, playback->start());
+ EXPECT_EQ(OK, cbPlayback->waitForAudioDeviceCb());
+ EXPECT_EQ(port_mix.id, playback->getAudioTrackHandle()->getRoutedDeviceId())
+ << "Playback NOT routed on expected port";
+
+ captureB->stop();
+
+ // check if mAddress submix out is disconnected as capture session is stopped
+ status = getPortByAttributes(AUDIO_PORT_ROLE_SINK, AUDIO_PORT_TYPE_DEVICE,
+ AUDIO_DEVICE_OUT_REMOTE_SUBMIX, mAddress, port_mix);
+ EXPECT_NE(OK, status) << "unexpected submix in port found";
+
+ // check if legacy submix out is connected
+ status = getPortByAttributes(AUDIO_PORT_ROLE_SINK, AUDIO_PORT_TYPE_DEVICE,
+ AUDIO_DEVICE_OUT_REMOTE_SUBMIX, "0", port);
+ EXPECT_EQ(OK, status) << "port not found";
+
+ // unregister policy
+ EXPECT_EQ(OK, AudioSystem::registerPolicyMixes(mMixes, false));
+ mPolicyMixRegistered = false;
+
+ // as policy mix is unregistered, expect submix in port with mAddress to be disconnected
+ status = getPortByAttributes(AUDIO_PORT_ROLE_SOURCE, AUDIO_PORT_TYPE_DEVICE,
+ AUDIO_DEVICE_IN_REMOTE_SUBMIX, mAddress, port_mix);
+ EXPECT_NE(OK, status) << "unexpected submix in port found";
+
+ playback->onProcess();
+ // as captureA is active, it should re route to legacy submix
+ EXPECT_EQ(OK, cbPlayback->waitForAudioDeviceCb(port.id));
+ EXPECT_EQ(port.id, playback->getAudioTrackHandle()->getRoutedDeviceId())
+ << "Playback NOT routed on expected port";
+
+ captureA->stop();
+ playback->stop();
+}
diff --git a/media/libeffects/lvm/wrapper/Aidl/EffectBundleAidl.cpp b/media/libeffects/lvm/wrapper/Aidl/EffectBundleAidl.cpp
index b1bf04c..f99bdd5 100644
--- a/media/libeffects/lvm/wrapper/Aidl/EffectBundleAidl.cpp
+++ b/media/libeffects/lvm/wrapper/Aidl/EffectBundleAidl.cpp
@@ -29,6 +29,7 @@
#include <LVM.h>
#include <limits.h>
+using aidl::android::hardware::audio::effect::Descriptor;
using aidl::android::hardware::audio::effect::EffectBundleAidl;
using aidl::android::hardware::audio::effect::kEqualizerBundleImplUUID;
using aidl::android::hardware::audio::effect::IEffect;
@@ -51,19 +52,12 @@
}
}
-extern "C" binder_exception_t destroyEffect(const std::shared_ptr<IEffect>& instanceSp) {
- if (!instanceSp) {
- LOG(ERROR) << __func__ << "nullInstance";
+extern "C" binder_exception_t queryEffect(const AudioUuid* in_impl_uuid, Descriptor* _aidl_return) {
+ if (!in_impl_uuid || *in_impl_uuid != kEqualizerBundleImplUUID) {
+ LOG(ERROR) << __func__ << "uuid not supported";
return EX_ILLEGAL_ARGUMENT;
}
- State state;
- ndk::ScopedAStatus status = instanceSp->getState(&state);
- if (!status.isOk() || State::INIT != state) {
- LOG(ERROR) << __func__ << " instance " << instanceSp.get()
- << " in state: " << toString(state) << ", status: " << status.getDescription();
- return EX_ILLEGAL_STATE;
- }
- LOG(DEBUG) << __func__ << " instance " << instanceSp.get() << " destroyed";
+ *_aidl_return = aidl::android::hardware::audio::effect::lvm::kEqualizerDesc;
return EX_NONE;
}
diff --git a/media/libstagefright/tests/HEVC/HEVCUtilsUnitTest.cpp b/media/libstagefright/tests/HEVC/HEVCUtilsUnitTest.cpp
index c43e1f8..19c8577 100644
--- a/media/libstagefright/tests/HEVC/HEVCUtilsUnitTest.cpp
+++ b/media/libstagefright/tests/HEVC/HEVCUtilsUnitTest.cpp
@@ -19,6 +19,7 @@
#include <utils/Log.h>
#include <fstream>
+#include <memory>
#include <media/stagefright/foundation/ABitReader.h>
#include <HevcUtils.h>
@@ -88,10 +89,10 @@
stringLine >> type >> chunkLength;
ASSERT_GT(chunkLength, 0) << "Length of data chunk must be greater than 0";
- char *data = (char *)malloc(chunkLength);
+ std::unique_ptr<char[]> data(new char[chunkLength]);
ASSERT_NE(data, nullptr) << "Failed to allocate data buffer of size: " << chunkLength;
- mMediaFileStream.read(data, chunkLength);
+ mMediaFileStream.read(data.get(), chunkLength);
ASSERT_EQ(mMediaFileStream.gcount(), chunkLength)
<< "Failed to read complete file, bytes read: " << mMediaFileStream.gcount();
@@ -105,7 +106,7 @@
offset += 3;
ASSERT_LE(offset, chunkLength) << "NAL unit offset must not exceed the chunk length";
- uint8_t *nalUnit = (uint8_t *)(data + offset);
+ uint8_t *nalUnit = (uint8_t *)(data.get() + offset);
size_t nalUnitLength = chunkLength - offset;
// Add NAL units only if they're of type: VPS/SPS/PPS/SEI
@@ -118,20 +119,18 @@
size_t sizeNalUnit = hevcParams.getSize(index);
ASSERT_EQ(sizeNalUnit, nalUnitLength) << "Invalid size returned for NAL: " << type;
- uint8_t *destination = (uint8_t *)malloc(nalUnitLength);
+ std::unique_ptr<uint8_t[]> destination(new uint8_t[nalUnitLength]);
ASSERT_NE(destination, nullptr)
<< "Failed to allocate buffer of size: " << nalUnitLength;
- bool status = hevcParams.write(index, destination, nalUnitLength);
+ bool status = hevcParams.write(index, destination.get(), nalUnitLength);
ASSERT_TRUE(status) << "Unable to write NAL Unit data";
- free(destination);
index++;
} else {
err = hevcParams.addNalUnit(nalUnit, nalUnitLength);
ASSERT_NE(err, (status_t)OK) << "Invalid NAL Unit added, type: " << type;
}
- free(data);
}
size_t numNalUnits = hevcParams.getNumNalUnitsOfType(kVPSCode);
@@ -166,10 +165,10 @@
<< "Expected NAL type: 34(PPS), found: " << typeNalUnit;
size_t hvccBoxSize = kHvccBoxMaxSize;
- uint8_t *hvcc = (uint8_t *)malloc(kHvccBoxMaxSize);
+ std::unique_ptr<uint8_t[]> hvcc(new uint8_t[kHvccBoxMaxSize]);
ASSERT_NE(hvcc, nullptr) << "Failed to allocate a hvcc buffer of size: " << kHvccBoxMaxSize;
- err = hevcParams.makeHvcc(hvcc, &hvccBoxSize, kNALSizeLength);
+ err = hevcParams.makeHvcc(hvcc.get(), &hvccBoxSize, kNALSizeLength);
ASSERT_EQ(err, (status_t)OK) << "Unable to create hvcc box";
ASSERT_GT(hvccBoxSize, kHvccBoxMinSize)
@@ -179,8 +178,6 @@
if (frameRate != mFrameRate)
cout << "[ WARN ] Expected frame rate: " << mFrameRate << " Found: " << frameRate
<< endl;
-
- free(hvcc);
}
// Info File contains the type and length for each chunk/frame
diff --git a/media/libstagefright/timedtext/test/TimedTextUnitTest.cpp b/media/libstagefright/timedtext/test/TimedTextUnitTest.cpp
index f934b54..b2044d3 100644
--- a/media/libstagefright/timedtext/test/TimedTextUnitTest.cpp
+++ b/media/libstagefright/timedtext/test/TimedTextUnitTest.cpp
@@ -22,6 +22,7 @@
#include <string.h>
#include <sys/stat.h>
#include <fstream>
+#include <memory>
#include <binder/Parcel.h>
#include <media/stagefright/foundation/AString.h>
@@ -240,10 +241,10 @@
if (remaining < tempFontNameLength) break;
const uint8_t *tmpFont = tmpData;
- char *tmpFontName = strndup((const char *)tmpFont, tempFontNameLength);
+ std::unique_ptr<char[]> tmpFontName(new char[tempFontNameLength]);
+ strncpy(tmpFontName.get(), (const char *)tmpFont, tempFontNameLength);
ASSERT_NE(tmpFontName, nullptr) << "Font Name is null";
- ALOGI("FontName = %s", tmpFontName);
- free(tmpFontName);
+ ALOGI("FontName = %s", tmpFontName.get());
tmpData += tempFontNameLength;
remaining -= tempFontNameLength;
fontRecordEntries.push_back({tempFontID, tempFontNameLength, tmpFont});
diff --git a/media/module/extractors/mp4/MPEG4Extractor.cpp b/media/module/extractors/mp4/MPEG4Extractor.cpp
index 2e889e3..8f8fc1b 100644
--- a/media/module/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/module/extractors/mp4/MPEG4Extractor.cpp
@@ -6723,7 +6723,7 @@
const Sample *smpl = &mCurrentSamples[mCurrentSampleIndex];
offset = smpl->offset;
size = smpl->size;
- cts = mCurrentTime + smpl->compositionOffset;
+ cts = (int64_t)mCurrentTime + (int64_t)smpl->compositionOffset;
if (mElstInitialEmptyEditTicks > 0) {
cts += mElstInitialEmptyEditTicks;