Merge "Move C2HandleIon and C2AllocationIon to internal dir"
diff --git a/media/codec2/TEST_MAPPING b/media/codec2/TEST_MAPPING
index 49614cd..8afa1a8 100644
--- a/media/codec2/TEST_MAPPING
+++ b/media/codec2/TEST_MAPPING
@@ -1,7 +1,6 @@
{
- "postsubmit": [
+ "presubmit": [
{
- // TODO: move to presubmit once we verify the tests are not flaky
"name": "CtsMediaTestCases",
"options": [
{
@@ -13,6 +12,9 @@
// TODO: b/149314419
{
"exclude-filter": "android.media.cts.AudioPlaybackCaptureTest"
+ },
+ {
+ "exclude-filter": "android.media.cts.AudioRecordTest"
}
]
}
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index 39263f9..0d093da 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -668,7 +668,7 @@
// initialize config here in case setParameters is called prior to configure
Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
const std::unique_ptr<Config> &config = *configLocked;
- status_t err = config->initialize(mClient, comp);
+ status_t err = config->initialize(mClient->getParamReflector(), comp);
if (err != OK) {
ALOGW("Failed to initialize configuration support");
// TODO: report error once we complete implementation.
@@ -884,6 +884,13 @@
}
}
+ int32_t subscribeToAllVendorParams;
+ if (msg->findInt32("x-*", &subscribeToAllVendorParams) && subscribeToAllVendorParams) {
+ if (config->subscribeToAllVendorParams(comp, C2_MAY_BLOCK) != OK) {
+ ALOGD("[%s] Failed to subscribe to all vendor params", comp->getName().c_str());
+ }
+ }
+
std::vector<std::unique_ptr<C2Param>> configUpdate;
// NOTE: We used to ignore "video-bitrate" at configure; replicate
// the behavior here.
@@ -1192,7 +1199,7 @@
// we are now using surface - apply default color aspects to input format - as well as
// get dataspace
- bool inputFormatChanged = config->updateFormats(config->IS_INPUT);
+ bool inputFormatChanged = config->updateFormats(Config::IS_INPUT);
ALOGD("input format %s to %s",
inputFormatChanged ? "changed" : "unchanged",
config->mInputFormat->debugString().c_str());
@@ -1207,7 +1214,7 @@
if (err != OK) {
// undo input format update
config->mUsingSurface = false;
- (void)config->updateFormats(config->IS_INPUT);
+ (void)config->updateFormats(Config::IS_INPUT);
return err;
}
config->mInputSurface = surface;
@@ -1323,6 +1330,8 @@
mCallback->onError(err2, ACTION_CODE_FATAL);
return;
}
+ // We're not starting after flush.
+ (void)mSentConfigAfterResume.test_and_set();
err2 = mChannel->start(inputFormat, outputFormat, buffersBoundToCodec);
if (err2 != OK) {
mCallback->onError(err2, ACTION_CODE_FATAL);
@@ -1555,18 +1564,27 @@
}
void CCodec::signalResume() {
- auto setResuming = [this] {
+ std::shared_ptr<Codec2Client::Component> comp;
+ auto setResuming = [this, &comp] {
Mutexed<State>::Locked state(mState);
if (state->get() != FLUSHED) {
return UNKNOWN_ERROR;
}
state->set(RESUMING);
+ comp = state->comp;
return OK;
};
if (tryAndReportOnError(setResuming) != OK) {
return;
}
+ mSentConfigAfterResume.clear();
+ {
+ Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
+ const std::unique_ptr<Config> &config = *configLocked;
+ config->queryConfiguration(comp);
+ }
+
(void)mChannel->start(nullptr, nullptr, [&]{
Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
const std::unique_ptr<Config> &config = *configLocked;
@@ -1617,7 +1635,8 @@
* Handle input surface parameters
*/
if ((config->mDomain & (Config::IS_VIDEO | Config::IS_IMAGE))
- && (config->mDomain & Config::IS_ENCODER) && config->mInputSurface && config->mISConfig) {
+ && (config->mDomain & Config::IS_ENCODER)
+ && config->mInputSurface && config->mISConfig) {
(void)params->findInt64(PARAMETER_KEY_OFFSET_TIME, &config->mISConfig->mTimeOffsetUs);
if (params->findInt64("skip-frames-before", &config->mISConfig->mStartAtUs)) {
@@ -1770,7 +1789,7 @@
// handle configuration changes in work done
Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
const std::unique_ptr<Config> &config = *configLocked;
- bool changed = false;
+ bool changed = !mSentConfigAfterResume.test_and_set();
Config::Watcher<C2StreamInitDataInfo::output> initData =
config->watch<C2StreamInitDataInfo::output>();
if (!work->worklets.empty()
@@ -1802,7 +1821,9 @@
++stream;
}
- changed = config->updateConfiguration(updates, config->mOutputDomain);
+ if (config->updateConfiguration(updates, config->mOutputDomain)) {
+ changed = true;
+ }
// copy standard infos to graphic buffers if not already present (otherwise, we
// may overwrite the actual intermediate value with a final value)
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 035dca8..b869bd9 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -1457,6 +1457,24 @@
std::unique_ptr<C2Work> work,
const sp<AMessage> &outputFormat,
const C2StreamInitDataInfo::output *initData) {
+ if (outputFormat != nullptr) {
+ Mutexed<Output>::Locked output(mOutput);
+ ALOGD("[%s] onWorkDone: output format changed to %s",
+ mName, outputFormat->debugString().c_str());
+ output->buffers->setFormat(outputFormat);
+
+ AString mediaType;
+ if (outputFormat->findString(KEY_MIME, &mediaType)
+ && mediaType == MIMETYPE_AUDIO_RAW) {
+ int32_t channelCount;
+ int32_t sampleRate;
+ if (outputFormat->findInt32(KEY_CHANNEL_COUNT, &channelCount)
+ && outputFormat->findInt32(KEY_SAMPLE_RATE, &sampleRate)) {
+ output->buffers->updateSkipCutBuffer(sampleRate, channelCount);
+ }
+ }
+ }
+
if ((work->input.ordinal.frameIndex - mFirstValidFrameIndex.load()).peek() < 0) {
// Discard frames from previous generation.
ALOGD("[%s] Discard frames from previous generation.", mName);
@@ -1634,24 +1652,6 @@
}
}
- if (outputFormat != nullptr) {
- Mutexed<Output>::Locked output(mOutput);
- ALOGD("[%s] onWorkDone: output format changed to %s",
- mName, outputFormat->debugString().c_str());
- output->buffers->setFormat(outputFormat);
-
- AString mediaType;
- if (outputFormat->findString(KEY_MIME, &mediaType)
- && mediaType == MIMETYPE_AUDIO_RAW) {
- int32_t channelCount;
- int32_t sampleRate;
- if (outputFormat->findInt32(KEY_CHANNEL_COUNT, &channelCount)
- && outputFormat->findInt32(KEY_SAMPLE_RATE, &sampleRate)) {
- output->buffers->updateSkipCutBuffer(sampleRate, channelCount);
- }
- }
- }
-
int32_t flags = 0;
if (worklet->output.flags & C2FrameData::FLAG_END_OF_STREAM) {
flags |= MediaCodec::BUFFER_FLAG_EOS;
diff --git a/media/codec2/sfplugin/CCodecConfig.cpp b/media/codec2/sfplugin/CCodecConfig.cpp
index b019d0d..051f88a 100644
--- a/media/codec2/sfplugin/CCodecConfig.cpp
+++ b/media/codec2/sfplugin/CCodecConfig.cpp
@@ -20,7 +20,6 @@
#include <log/log.h>
#include <C2Component.h>
-#include <C2Debug.h>
#include <C2Param.h>
#include <util/C2InterfaceHelper.h>
@@ -51,6 +50,27 @@
namespace {
+void C2ValueToMessageItem(const C2Value &value, AMessage::ItemData &item) {
+ int32_t int32Value;
+ uint32_t uint32Value;
+ int64_t int64Value;
+ uint64_t uint64Value;
+ float floatValue;
+ if (value.get(&int32Value)) {
+ item.set(int32Value);
+ } else if (value.get(&uint32Value) && uint32Value <= uint32_t(INT32_MAX)) {
+ // SDK does not support unsigned values
+ item.set((int32_t)uint32Value);
+ } else if (value.get(&int64Value)) {
+ item.set(int64Value);
+ } else if (value.get(&uint64Value) && uint64Value <= uint64_t(INT64_MAX)) {
+ // SDK does not support unsigned values
+ item.set((int64_t)uint64Value);
+ } else if (value.get(&floatValue)) {
+ item.set(floatValue);
+ }
+}
+
/**
* mapping between SDK and Codec 2.0 configurations.
*/
@@ -140,27 +160,10 @@
/// Maps from a C2Value to an SDK value in an AMessage.
AMessage::ItemData mapToMessage(C2Value value) const {
AMessage::ItemData item;
- int32_t int32Value;
- uint32_t uint32Value;
- int64_t int64Value;
- uint64_t uint64Value;
- float floatValue;
if (value.type() != C2Value::NO_INIT && mReverse) {
value = mReverse(value);
}
- if (value.get(&int32Value)) {
- item.set(int32Value);
- } else if (value.get(&uint32Value) && uint32Value <= uint32_t(INT32_MAX)) {
- // SDK does not support unsigned values
- item.set((int32_t)uint32Value);
- } else if (value.get(&int64Value)) {
- item.set(int64Value);
- } else if (value.get(&uint64Value) && uint64Value <= uint64_t(INT64_MAX)) {
- // SDK does not support unsigned values
- item.set((int64_t)uint64Value);
- } else if (value.get(&floatValue)) {
- item.set(floatValue);
- }
+ C2ValueToMessageItem(value, item);
return item;
}
@@ -181,10 +184,10 @@
template <typename PORT, typename STREAM>
AString QueryMediaTypeImpl(
- const std::shared_ptr<Codec2Client::Component> &component) {
+ const std::shared_ptr<Codec2Client::Configurable> &configurable) {
AString mediaType;
std::vector<std::unique_ptr<C2Param>> queried;
- c2_status_t c2err = component->query(
+ c2_status_t c2err = configurable->query(
{}, { PORT::PARAM_TYPE, STREAM::PARAM_TYPE }, C2_DONT_BLOCK, &queried);
if (c2err != C2_OK && queried.size() == 0) {
ALOGD("Query media type failed => %s", asString(c2err));
@@ -209,13 +212,13 @@
}
AString QueryMediaType(
- bool input, const std::shared_ptr<Codec2Client::Component> &component) {
+ bool input, const std::shared_ptr<Codec2Client::Configurable> &configurable) {
typedef C2PortMediaTypeSetting P;
typedef C2StreamMediaTypeSetting S;
if (input) {
- return QueryMediaTypeImpl<P::input, S::input>(component);
+ return QueryMediaTypeImpl<P::input, S::input>(configurable);
} else {
- return QueryMediaTypeImpl<P::output, S::output>(component);
+ return QueryMediaTypeImpl<P::output, S::output>(configurable);
}
}
@@ -912,27 +915,27 @@
}
status_t CCodecConfig::initialize(
- const std::shared_ptr<Codec2Client> &client,
- const std::shared_ptr<Codec2Client::Component> &component) {
+ const std::shared_ptr<C2ParamReflector> &reflector,
+ const std::shared_ptr<Codec2Client::Configurable> &configurable) {
C2ComponentDomainSetting domain(C2Component::DOMAIN_OTHER);
C2ComponentKindSetting kind(C2Component::KIND_OTHER);
std::vector<std::unique_ptr<C2Param>> queried;
- c2_status_t c2err = component->query({ &domain, &kind }, {}, C2_DONT_BLOCK, &queried);
+ c2_status_t c2err = configurable->query({ &domain, &kind }, {}, C2_DONT_BLOCK, &queried);
if (c2err != C2_OK) {
ALOGD("Query domain & kind failed => %s", asString(c2err));
// TEMP: determine kind from component name
if (kind.value == C2Component::KIND_OTHER) {
- if (component->getName().find("encoder") != std::string::npos) {
+ if (configurable->getName().find("encoder") != std::string::npos) {
kind.value = C2Component::KIND_ENCODER;
- } else if (component->getName().find("decoder") != std::string::npos) {
+ } else if (configurable->getName().find("decoder") != std::string::npos) {
kind.value = C2Component::KIND_DECODER;
}
}
// TEMP: determine domain from media type (port (preferred) or stream #0)
if (domain.value == C2Component::DOMAIN_OTHER) {
- AString mediaType = QueryMediaType(true /* input */, component);
+ AString mediaType = QueryMediaType(true /* input */, configurable);
if (mediaType.startsWith("audio/")) {
domain.value = C2Component::DOMAIN_AUDIO;
} else if (mediaType.startsWith("video/")) {
@@ -957,16 +960,16 @@
std::vector<C2Param::Index> paramIndices;
switch (kind.value) {
case C2Component::KIND_DECODER:
- mCodingMediaType = QueryMediaType(true /* input */, component).c_str();
+ mCodingMediaType = QueryMediaType(true /* input */, configurable).c_str();
break;
case C2Component::KIND_ENCODER:
- mCodingMediaType = QueryMediaType(false /* input */, component).c_str();
+ mCodingMediaType = QueryMediaType(false /* input */, configurable).c_str();
break;
default:
mCodingMediaType = "";
}
- c2err = component->querySupportedParams(&mParamDescs);
+ c2err = configurable->querySupportedParams(&mParamDescs);
if (c2err != C2_OK) {
ALOGD("Query supported params failed after returning %zu values => %s",
mParamDescs.size(), asString(c2err));
@@ -976,9 +979,9 @@
mSupportedIndices.emplace(desc->index());
}
- mReflector = client->getParamReflector();
+ mReflector = reflector;
if (mReflector == nullptr) {
- ALOGE("Failed to get param reflector");
+ ALOGE("Null param reflector");
return UNKNOWN_ERROR;
}
@@ -1032,11 +1035,21 @@
// init data (CSD)
mSubscribedIndices.emplace(C2StreamInitDataInfo::output::PARAM_TYPE);
+ for (const std::shared_ptr<C2ParamDescriptor> &desc : mParamDescs) {
+ if (desc->index().isVendor()) {
+ std::vector<std::string> keys;
+ mParamUpdater->getKeysForParamIndex(desc->index(), &keys);
+ for (const std::string &key : keys) {
+ mVendorParamIndices.insert_or_assign(key, desc->index());
+ }
+ }
+ }
+
return OK;
}
status_t CCodecConfig::subscribeToConfigUpdate(
- const std::shared_ptr<Codec2Client::Component> &component,
+ const std::shared_ptr<Codec2Client::Configurable> &configurable,
const std::vector<C2Param::Index> &indices,
c2_blocking_t blocking) {
mSubscribedIndices.insert(indices.begin(), indices.end());
@@ -1049,7 +1062,7 @@
std::unique_ptr<C2SubscribedParamIndicesTuning> subscribeTuning =
C2SubscribedParamIndicesTuning::AllocUnique(indices);
std::vector<std::unique_ptr<C2SettingResult>> results;
- c2_status_t c2Err = component->config({ subscribeTuning.get() }, blocking, &results);
+ c2_status_t c2Err = configurable->config({ subscribeTuning.get() }, blocking, &results);
if (c2Err != C2_OK && c2Err != C2_BAD_INDEX) {
ALOGD("Failed to subscribe to parameters => %s", asString(c2Err));
// TODO: error
@@ -1061,11 +1074,11 @@
}
status_t CCodecConfig::queryConfiguration(
- const std::shared_ptr<Codec2Client::Component> &component) {
+ const std::shared_ptr<Codec2Client::Configurable> &configurable) {
// query all subscribed parameters
std::vector<C2Param::Index> indices(mSubscribedIndices.begin(), mSubscribedIndices.end());
std::vector<std::unique_ptr<C2Param>> queried;
- c2_status_t c2Err = component->query({}, indices, C2_MAY_BLOCK, &queried);
+ c2_status_t c2Err = configurable->query({}, indices, C2_MAY_BLOCK, &queried);
if (c2Err != OK) {
ALOGI("query failed after returning %zu values (%s)", queried.size(), asString(c2Err));
// TODO: error
@@ -1135,7 +1148,7 @@
if (domain & mInputDomain) {
sp<AMessage> oldFormat = mInputFormat;
mInputFormat = mInputFormat->dup(); // trigger format changed
- mInputFormat->extend(getSdkFormatForDomain(reflected, mInputDomain));
+ mInputFormat->extend(getFormatForDomain(reflected, mInputDomain));
if (mInputFormat->countEntries() != oldFormat->countEntries()
|| mInputFormat->changesFrom(oldFormat)->countEntries() > 0) {
changed = true;
@@ -1146,7 +1159,7 @@
if (domain & mOutputDomain) {
sp<AMessage> oldFormat = mOutputFormat;
mOutputFormat = mOutputFormat->dup(); // trigger output format changed
- mOutputFormat->extend(getSdkFormatForDomain(reflected, mOutputDomain));
+ mOutputFormat->extend(getFormatForDomain(reflected, mOutputDomain));
if (mOutputFormat->countEntries() != oldFormat->countEntries()
|| mOutputFormat->changesFrom(oldFormat)->countEntries() > 0) {
changed = true;
@@ -1158,8 +1171,9 @@
return changed;
}
-sp<AMessage> CCodecConfig::getSdkFormatForDomain(
- const ReflectedParamUpdater::Dict &reflected, Domain portDomain) const {
+sp<AMessage> CCodecConfig::getFormatForDomain(
+ const ReflectedParamUpdater::Dict &reflected,
+ Domain portDomain) const {
sp<AMessage> msg = new AMessage;
for (const std::pair<std::string, std::vector<ConfigMapper>> &el : mStandardParams->getKeys()) {
for (const ConfigMapper &cm : el.second) {
@@ -1190,6 +1204,39 @@
}
}
+ bool input = (portDomain & Domain::IS_INPUT);
+ std::vector<std::string> vendorKeys;
+ for (const std::pair<std::string, ReflectedParamUpdater::Value> &entry : reflected) {
+ auto it = mVendorParamIndices.find(entry.first);
+ if (it == mVendorParamIndices.end()) {
+ continue;
+ }
+ if (mSubscribedIndices.count(it->second) == 0) {
+ continue;
+ }
+ // For vendor parameters, we only care about direction
+ if ((input && !it->second.forInput())
+ || (!input && !it->second.forOutput())) {
+ continue;
+ }
+ const ReflectedParamUpdater::Value &value = entry.second;
+ C2Value c2Value;
+ sp<ABuffer> bufValue;
+ AString strValue;
+ AMessage::ItemData item;
+ if (value.find(&c2Value)) {
+ C2ValueToMessageItem(c2Value, item);
+ } else if (value.find(&bufValue)) {
+ item.set(bufValue);
+ } else if (value.find(&strValue)) {
+ item.set(strValue);
+ } else {
+ ALOGD("unexpected untyped query value for key: %s", entry.first.c_str());
+ continue;
+ }
+ msg->setItem(entry.first.c_str(), item);
+ }
+
{ // convert from Codec 2.0 rect to MediaFormat rect and add crop rect if not present
int32_t left, top, width, height;
if (msg->findInt32("crop-left", &left) && msg->findInt32("crop-width", &width)
@@ -1597,7 +1644,7 @@
}
status_t CCodecConfig::getConfigUpdateFromSdkParams(
- std::shared_ptr<Codec2Client::Component> component,
+ std::shared_ptr<Codec2Client::Configurable> configurable,
const sp<AMessage> &sdkParams, Domain configDomain,
c2_blocking_t blocking,
std::vector<std::unique_ptr<C2Param>> *configUpdate) const {
@@ -1624,7 +1671,7 @@
}
}
- c2_status_t err = component->query({ }, supportedIndices, blocking, configUpdate);
+ c2_status_t err = configurable->query({ }, supportedIndices, blocking, configUpdate);
if (err != C2_OK) {
ALOGD("query failed after returning %zu params => %s", configUpdate->size(), asString(err));
}
@@ -1636,7 +1683,7 @@
}
status_t CCodecConfig::setParameters(
- std::shared_ptr<Codec2Client::Component> component,
+ std::shared_ptr<Codec2Client::Configurable> configurable,
std::vector<std::unique_ptr<C2Param>> &configUpdate,
c2_blocking_t blocking) {
status_t result = OK;
@@ -1672,10 +1719,10 @@
}
}
// update subscribed param indices
- subscribeToConfigUpdate(component, indices, blocking);
+ subscribeToConfigUpdate(configurable, indices, blocking);
std::vector<std::unique_ptr<C2SettingResult>> failures;
- c2_status_t err = component->config(paramVector, blocking, &failures);
+ c2_status_t err = configurable->config(paramVector, blocking, &failures);
if (err != C2_OK) {
ALOGD("config failed => %s", asString(err));
// This is non-fatal.
@@ -1695,7 +1742,7 @@
// Re-query parameter values in case config could not update them and update the current
// configuration.
configUpdate.clear();
- err = component->query({}, indices, blocking, &configUpdate);
+ err = configurable->query({}, indices, blocking, &configUpdate);
if (err != C2_OK) {
ALOGD("query failed after returning %zu params => %s", configUpdate.size(), asString(err));
}
@@ -1714,4 +1761,13 @@
}
}
+status_t CCodecConfig::subscribeToAllVendorParams(
+ const std::shared_ptr<Codec2Client::Configurable> &configurable,
+ c2_blocking_t blocking) {
+ for (const std::pair<std::string, C2Param::Index> &entry : mVendorParamIndices) {
+ mSubscribedIndices.insert(entry.second);
+ }
+ return subscribeToConfigUpdate(configurable, {}, blocking);
+}
+
} // namespace android
diff --git a/media/codec2/sfplugin/CCodecConfig.h b/media/codec2/sfplugin/CCodecConfig.h
index 093bfdd..2895746 100644
--- a/media/codec2/sfplugin/CCodecConfig.h
+++ b/media/codec2/sfplugin/CCodecConfig.h
@@ -23,8 +23,10 @@
#include <vector>
#include <C2Component.h>
-#include <codec2/hidl/client.h>
+#include <C2Config.h>
+#include <C2Debug.h>
+#include <codec2/hidl/client.h>
#include <utils/RefBase.h>
#include "InputSurfaceWrapper.h"
@@ -39,7 +41,6 @@
* Struct managing the codec configuration for CCodec.
*/
struct CCodecConfig {
-
/**
* Domain consists of a bitmask divided into fields, and specifiers work by excluding other
* values in those domains.
@@ -135,6 +136,9 @@
/// For now support a validation function.
std::map<C2Param::Index, LocalParamValidator> mLocalParams;
+ /// Vendor field name -> index map.
+ std::map<std::string, C2Param::Index> mVendorParamIndices;
+
std::set<std::string> mLastConfig;
CCodecConfig();
@@ -143,9 +147,8 @@
/// reflected param helper, domain, standard params, and subscribes to standard
/// indices.
status_t initialize(
- const std::shared_ptr<Codec2Client> &client,
- const std::shared_ptr<Codec2Client::Component> &component);
-
+ const std::shared_ptr<C2ParamReflector> &client,
+ const std::shared_ptr<Codec2Client::Configurable> &configurable);
/**
* Adds a locally maintained parameter. This is used for output configuration that can be
@@ -238,7 +241,7 @@
* \param blocking blocking mode to use with the component
*/
status_t getConfigUpdateFromSdkParams(
- std::shared_ptr<Codec2Client::Component> component,
+ std::shared_ptr<Codec2Client::Configurable> configurable,
const sp<AMessage> &sdkParams, Domain domain,
c2_blocking_t blocking,
std::vector<std::unique_ptr<C2Param>> *configUpdate) const;
@@ -250,19 +253,24 @@
* \param blocking blocking mode to use with the component
*/
status_t setParameters(
- std::shared_ptr<Codec2Client::Component> component,
+ std::shared_ptr<Codec2Client::Configurable> configurable,
std::vector<std::unique_ptr<C2Param>> &configUpdate,
c2_blocking_t blocking);
/// Queries subscribed indices (which contains all SDK-exposed values) and updates
/// input/output formats.
status_t queryConfiguration(
- const std::shared_ptr<Codec2Client::Component> &component);
+ const std::shared_ptr<Codec2Client::Configurable> &configurable);
/// Queries a configuration parameter value. Returns nullptr if the parameter is not
/// part of the current configuration
const C2Param *getConfigParameterValue(C2Param::Index index) const;
+ /// Subscribe to all vendor parameters.
+ status_t subscribeToAllVendorParams(
+ const std::shared_ptr<Codec2Client::Configurable> &configurable,
+ c2_blocking_t blocking);
+
/**
* Object that can be used to access configuration parameters and if they change.
*/
@@ -321,14 +329,15 @@
/// Adds indices to the subscribed indices, and updated subscription to component
/// \param blocking blocking mode to use with the component
status_t subscribeToConfigUpdate(
- const std::shared_ptr<Codec2Client::Component> &component,
+ const std::shared_ptr<Codec2Client::Configurable> &configurable,
const std::vector<C2Param::Index> &indices,
c2_blocking_t blocking = C2_DONT_BLOCK);
/// Gets SDK format from codec 2.0 reflected configuration
/// \param domain input/output bitmask
- sp<AMessage> getSdkFormatForDomain(
- const ReflectedParamUpdater::Dict &reflected, Domain domain) const;
+ sp<AMessage> getFormatForDomain(
+ const ReflectedParamUpdater::Dict &reflected,
+ Domain domain) const;
/**
* Converts a set of configuration parameters in an AMessage to a list of path-based Codec
diff --git a/media/codec2/sfplugin/ReflectedParamUpdater.cpp b/media/codec2/sfplugin/ReflectedParamUpdater.cpp
index 55b0ec9..f39051b 100644
--- a/media/codec2/sfplugin/ReflectedParamUpdater.cpp
+++ b/media/codec2/sfplugin/ReflectedParamUpdater.cpp
@@ -125,18 +125,6 @@
}
addParamDesc(desc, *structDesc, reflector, true /* markVendor */);
}
-
- // TEMP: also add vendor parameters as non-vendor
- for (const std::shared_ptr<C2ParamDescriptor> &desc : paramDescs) {
- if (!desc->index().isVendor()) {
- continue;
- }
- std::unique_ptr<C2StructDescriptor> structDesc = reflector->describe(
- desc->index().coreIndex());
- if (structDesc) {
- addParamDesc(desc, *structDesc, reflector, false /* markVendor */);
- }
- }
}
void ReflectedParamUpdater::addParamStructDesc(
@@ -286,6 +274,20 @@
}
}
+void ReflectedParamUpdater::getKeysForParamIndex(
+ const C2Param::Index &index,
+ std::vector<std::string> *keys /* nonnull */) const {
+ CHECK(keys != nullptr);
+ keys->clear();
+ for (const std::pair<const std::string, FieldDesc> &kv : mMap) {
+ const std::string &name = kv.first;
+ const FieldDesc &desc = kv.second;
+ if (desc.paramDesc->index() == index) {
+ keys->push_back(name);
+ }
+ }
+}
+
void ReflectedParamUpdater::updateParamsFromMessage(
const Dict ¶ms,
std::vector<std::unique_ptr<C2Param>> *vec /* nonnull */) const {
diff --git a/media/codec2/sfplugin/ReflectedParamUpdater.h b/media/codec2/sfplugin/ReflectedParamUpdater.h
index 5436ba5..752c7e4 100644
--- a/media/codec2/sfplugin/ReflectedParamUpdater.h
+++ b/media/codec2/sfplugin/ReflectedParamUpdater.h
@@ -166,6 +166,16 @@
std::vector<C2Param::Index> *vec /* nonnull */) const;
/**
+ * Get list of field names for the given param index.
+ *
+ * \param index[in] param index
+ * \param keys[out] vector to store the field names
+ */
+ void getKeysForParamIndex(
+ const C2Param::Index &index,
+ std::vector<std::string> *keys /* nonnull */) const;
+
+ /**
* Update C2Param objects from field name and value in AMessage object.
*
* \param params[in] Dict object with field name to value pairs.
diff --git a/media/codec2/sfplugin/include/media/stagefright/CCodec.h b/media/codec2/sfplugin/include/media/stagefright/CCodec.h
index 6ff2c4a..ecb2506 100644
--- a/media/codec2/sfplugin/include/media/stagefright/CCodec.h
+++ b/media/codec2/sfplugin/include/media/stagefright/CCodec.h
@@ -17,6 +17,7 @@
#ifndef C_CODEC_H_
#define C_CODEC_H_
+#include <atomic>
#include <chrono>
#include <list>
#include <memory>
@@ -189,9 +190,10 @@
struct ClientListener;
Mutexed<NamedTimePoint> mDeadline;
- typedef CCodecConfig Config;
+
Mutexed<std::unique_ptr<CCodecConfig>> mConfig;
Mutexed<std::list<std::unique_ptr<C2Work>>> mWorkDoneQueue;
+ std::atomic_flag mSentConfigAfterResume;
friend class CCodecCallbackImpl;
diff --git a/media/codec2/sfplugin/tests/Android.bp b/media/codec2/sfplugin/tests/Android.bp
index c953f84..fe5fa68 100644
--- a/media/codec2/sfplugin/tests/Android.bp
+++ b/media/codec2/sfplugin/tests/Android.bp
@@ -2,16 +2,24 @@
name: "ccodec_unit_test",
srcs: [
+ "CCodecConfig_test.cpp",
"ReflectedParamUpdater_test.cpp",
],
+ defaults: [
+ "libcodec2-hidl-defaults@1.0",
+ "libcodec2-internal-defaults",
+ ],
+
include_dirs: [
"frameworks/av/media/codec2/sfplugin",
],
shared_libs: [
"libcodec2",
+ "libcodec2_client",
"libsfplugin_ccodec",
+ "libsfplugin_ccodec_utils",
"libstagefright_foundation",
"libutils",
],
diff --git a/media/codec2/sfplugin/tests/CCodecConfig_test.cpp b/media/codec2/sfplugin/tests/CCodecConfig_test.cpp
new file mode 100644
index 0000000..7b445a0
--- /dev/null
+++ b/media/codec2/sfplugin/tests/CCodecConfig_test.cpp
@@ -0,0 +1,311 @@
+/*
+ * 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 "CCodecConfig.h"
+
+#include <set>
+
+#include <gtest/gtest.h>
+
+#include <codec2/hidl/1.0/Configurable.h>
+#include <codec2/hidl/client.h>
+#include <util/C2InterfaceHelper.h>
+
+namespace {
+
+enum ExtendedC2ParamIndexKind : C2Param::type_index_t {
+ kParamIndexVendorInt32 = C2Param::TYPE_INDEX_VENDOR_START,
+ kParamIndexVendorInt64,
+ kParamIndexVendorString,
+};
+
+typedef C2PortParam<C2Info, C2Int32Value, kParamIndexVendorInt32> C2PortVendorInt32Info;
+constexpr char C2_PARAMKEY_VENDOR_INT32[] = "example.int32";
+constexpr char KEY_VENDOR_INT32[] = "vendor.example.int32.value";
+
+typedef C2StreamParam<C2Info, C2Int64Value, kParamIndexVendorInt64> C2StreamVendorInt64Info;
+constexpr char C2_PARAMKEY_VENDOR_INT64[] = "example.int64";
+constexpr char KEY_VENDOR_INT64[] = "vendor.example.int64.value";
+
+typedef C2PortParam<C2Info, C2StringValue, kParamIndexVendorString> C2PortVendorStringInfo;
+constexpr char C2_PARAMKEY_VENDOR_STRING[] = "example.string";
+constexpr char KEY_VENDOR_STRING[] = "vendor.example.string.value";
+
+} // namespace
+
+namespace android {
+
+class CCodecConfigTest : public ::testing::Test {
+public:
+ constexpr static int32_t kCodec2Int32 = 0xC0DEC2;
+ constexpr static int64_t kCodec2Int64 = 0xC0DEC2C0DEC2ll;
+ constexpr static char kCodec2Str[] = "codec2";
+
+ CCodecConfigTest()
+ : mReflector{std::make_shared<C2ReflectorHelper>()} {
+ sp<hardware::media::c2::V1_0::utils::CachedConfigurable> cachedConfigurable =
+ new hardware::media::c2::V1_0::utils::CachedConfigurable(
+ std::make_unique<Configurable>(mReflector));
+ cachedConfigurable->init(std::make_shared<Cache>());
+ mConfigurable = std::make_shared<Codec2Client::Configurable>(cachedConfigurable);
+ }
+
+ struct Cache : public hardware::media::c2::V1_0::utils::ParameterCache {
+ c2_status_t validate(const std::vector<std::shared_ptr<C2ParamDescriptor>>&) override {
+ return C2_OK;
+ }
+ };
+
+ class Configurable : public hardware::media::c2::V1_0::utils::ConfigurableC2Intf {
+ public:
+ explicit Configurable(const std::shared_ptr<C2ReflectorHelper> &reflector)
+ : ConfigurableC2Intf("name", 0u),
+ mImpl(reflector) {
+ }
+
+ c2_status_t query(
+ const std::vector<C2Param::Index> &indices,
+ c2_blocking_t mayBlock,
+ std::vector<std::unique_ptr<C2Param>>* const params) const override {
+ return mImpl.query({}, indices, mayBlock, params);
+ }
+
+ c2_status_t config(
+ const std::vector<C2Param*> ¶ms,
+ c2_blocking_t mayBlock,
+ std::vector<std::unique_ptr<C2SettingResult>>* const failures) override {
+ return mImpl.config(params, mayBlock, failures);
+ }
+
+ c2_status_t querySupportedParams(
+ std::vector<std::shared_ptr<C2ParamDescriptor>>* const params) const override {
+ return mImpl.querySupportedParams(params);
+ }
+
+ c2_status_t querySupportedValues(
+ std::vector<C2FieldSupportedValuesQuery>& fields,
+ c2_blocking_t mayBlock) const override {
+ return mImpl.querySupportedValues(fields, mayBlock);
+ }
+
+ private:
+ class Impl : public C2InterfaceHelper {
+ public:
+ explicit Impl(const std::shared_ptr<C2ReflectorHelper> &reflector)
+ : C2InterfaceHelper{reflector} {
+ setDerivedInstance(this);
+
+ addParameter(
+ DefineParam(mInt32Input, C2_PARAMKEY_VENDOR_INT32)
+ .withDefault(new C2PortVendorInt32Info::input(0))
+ .withFields({C2F(mInt32Input, value).any()})
+ .withSetter(Setter<decltype(mInt32Input)::element_type>)
+ .build());
+
+ addParameter(
+ DefineParam(mInt64Output, C2_PARAMKEY_VENDOR_INT64)
+ .withDefault(new C2StreamVendorInt64Info::output(0u, 0))
+ .withFields({C2F(mInt64Output, value).any()})
+ .withSetter(Setter<decltype(mInt64Output)::element_type>)
+ .build());
+
+ addParameter(
+ DefineParam(mStringInput, C2_PARAMKEY_VENDOR_STRING)
+ .withDefault(decltype(mStringInput)::element_type::AllocShared(1, ""))
+ .withFields({C2F(mStringInput, m.value).any()})
+ .withSetter(Setter<decltype(mStringInput)::element_type>)
+ .build());
+
+ // TODO: SDK params
+ }
+ private:
+ std::shared_ptr<C2PortVendorInt32Info::input> mInt32Input;
+ std::shared_ptr<C2StreamVendorInt64Info::output> mInt64Output;
+ std::shared_ptr<C2PortVendorStringInfo::input> mStringInput;
+
+ template<typename T>
+ static C2R Setter(bool, C2P<T> &) {
+ return C2R::Ok();
+ }
+ };
+
+ Impl mImpl;
+ };
+
+ std::shared_ptr<C2ReflectorHelper> mReflector;
+ std::shared_ptr<Codec2Client::Configurable> mConfigurable;
+ CCodecConfig mConfig;
+};
+
+using D = CCodecConfig::Domain;
+
+template<typename T>
+T *FindParam(const std::vector<std::unique_ptr<C2Param>> &vec) {
+ for (const std::unique_ptr<C2Param> ¶m : vec) {
+ if (param->coreIndex() == T::CORE_INDEX) {
+ return static_cast<T *>(param.get());
+ }
+ }
+ return nullptr;
+}
+
+TEST_F(CCodecConfigTest, SetVendorParam) {
+ ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable));
+
+ sp<AMessage> format{new AMessage};
+ format->setInt32(KEY_VENDOR_INT32, kCodec2Int32);
+ format->setInt64(KEY_VENDOR_INT64, kCodec2Int64);
+ format->setString(KEY_VENDOR_STRING, kCodec2Str);
+
+ std::vector<std::unique_ptr<C2Param>> configUpdate;
+ ASSERT_EQ(OK, mConfig.getConfigUpdateFromSdkParams(
+ mConfigurable, format, D::IS_INPUT | D::IS_OUTPUT, C2_MAY_BLOCK, &configUpdate));
+
+ ASSERT_EQ(3u, configUpdate.size());
+ C2PortVendorInt32Info::input *i32 =
+ FindParam<std::remove_pointer<decltype(i32)>::type>(configUpdate);
+ ASSERT_NE(nullptr, i32);
+ ASSERT_EQ(kCodec2Int32, i32->value);
+
+ C2StreamVendorInt64Info::output *i64 =
+ FindParam<std::remove_pointer<decltype(i64)>::type>(configUpdate);
+ ASSERT_NE(nullptr, i64);
+ ASSERT_EQ(kCodec2Int64, i64->value);
+
+ C2PortVendorStringInfo::input *str =
+ FindParam<std::remove_pointer<decltype(str)>::type>(configUpdate);
+ ASSERT_NE(nullptr, str);
+ ASSERT_STREQ(kCodec2Str, str->m.value);
+}
+
+TEST_F(CCodecConfigTest, VendorParamUpdate_Unsubscribed) {
+ ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable));
+
+ std::vector<std::unique_ptr<C2Param>> configUpdate;
+ C2PortVendorInt32Info::input i32(kCodec2Int32);
+ C2StreamVendorInt64Info::output i64(0u, kCodec2Int64);
+ std::unique_ptr<C2PortVendorStringInfo::input> str =
+ C2PortVendorStringInfo::input::AllocUnique(strlen(kCodec2Str) + 1, kCodec2Str);
+ configUpdate.push_back(C2Param::Copy(i32));
+ configUpdate.push_back(C2Param::Copy(i64));
+ configUpdate.push_back(std::move(str));
+
+ // The vendor parameters are not yet subscribed
+ ASSERT_FALSE(mConfig.updateConfiguration(configUpdate, D::IS_INPUT | D::IS_OUTPUT));
+
+ int32_t vendorInt32{0};
+ ASSERT_FALSE(mConfig.mInputFormat->findInt32(KEY_VENDOR_INT32, &vendorInt32))
+ << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str();
+ ASSERT_FALSE(mConfig.mOutputFormat->findInt32(KEY_VENDOR_INT32, &vendorInt32))
+ << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+
+ int64_t vendorInt64{0};
+ ASSERT_FALSE(mConfig.mInputFormat->findInt64(KEY_VENDOR_INT64, &vendorInt64))
+ << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str();
+ ASSERT_FALSE(mConfig.mOutputFormat->findInt64(KEY_VENDOR_INT64, &vendorInt64))
+ << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+
+ AString vendorString;
+ ASSERT_FALSE(mConfig.mInputFormat->findString(KEY_VENDOR_STRING, &vendorString))
+ << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str();
+ ASSERT_FALSE(mConfig.mOutputFormat->findString(KEY_VENDOR_STRING, &vendorString))
+ << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+}
+
+TEST_F(CCodecConfigTest, VendorParamUpdate_AllSubscribed) {
+ ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable));
+
+ // Force subscribe to all vendor params
+ ASSERT_EQ(OK, mConfig.subscribeToAllVendorParams(mConfigurable, C2_MAY_BLOCK));
+
+ std::vector<std::unique_ptr<C2Param>> configUpdate;
+ C2PortVendorInt32Info::input i32(kCodec2Int32);
+ C2StreamVendorInt64Info::output i64(0u, kCodec2Int64);
+ std::unique_ptr<C2PortVendorStringInfo::input> str =
+ C2PortVendorStringInfo::input::AllocUnique(strlen(kCodec2Str) + 1, kCodec2Str);
+ configUpdate.push_back(C2Param::Copy(i32));
+ configUpdate.push_back(C2Param::Copy(i64));
+ configUpdate.push_back(std::move(str));
+
+ ASSERT_TRUE(mConfig.updateConfiguration(configUpdate, D::IS_INPUT | D::IS_OUTPUT));
+
+ int32_t vendorInt32{0};
+ ASSERT_TRUE(mConfig.mInputFormat->findInt32(KEY_VENDOR_INT32, &vendorInt32))
+ << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str();
+ ASSERT_EQ(kCodec2Int32, vendorInt32);
+ ASSERT_FALSE(mConfig.mOutputFormat->findInt32(KEY_VENDOR_INT32, &vendorInt32))
+ << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+
+ int64_t vendorInt64{0};
+ ASSERT_FALSE(mConfig.mInputFormat->findInt64(KEY_VENDOR_INT64, &vendorInt64))
+ << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str();
+ ASSERT_TRUE(mConfig.mOutputFormat->findInt64(KEY_VENDOR_INT64, &vendorInt64))
+ << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+ ASSERT_EQ(kCodec2Int64, vendorInt64);
+
+ AString vendorString;
+ ASSERT_TRUE(mConfig.mInputFormat->findString(KEY_VENDOR_STRING, &vendorString))
+ << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str();
+ ASSERT_STREQ(kCodec2Str, vendorString.c_str());
+ ASSERT_FALSE(mConfig.mOutputFormat->findString(KEY_VENDOR_STRING, &vendorString))
+ << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+}
+
+TEST_F(CCodecConfigTest, VendorParamUpdate_PartiallySubscribed) {
+ ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable));
+
+ // Subscribe to example.int32 only
+ std::vector<std::unique_ptr<C2Param>> configUpdate;
+ sp<AMessage> format{new AMessage};
+ format->setInt32(KEY_VENDOR_INT32, 0);
+ configUpdate.clear();
+ ASSERT_EQ(OK, mConfig.getConfigUpdateFromSdkParams(
+ mConfigurable, format, D::IS_INPUT | D::IS_OUTPUT, C2_MAY_BLOCK, &configUpdate));
+ ASSERT_EQ(OK, mConfig.setParameters(mConfigurable, configUpdate, C2_MAY_BLOCK));
+
+ C2PortVendorInt32Info::input i32(kCodec2Int32);
+ C2StreamVendorInt64Info::output i64(0u, kCodec2Int64);
+ std::unique_ptr<C2PortVendorStringInfo::input> str =
+ C2PortVendorStringInfo::input::AllocUnique(strlen(kCodec2Str) + 1, kCodec2Str);
+ configUpdate.clear();
+ configUpdate.push_back(C2Param::Copy(i32));
+ configUpdate.push_back(C2Param::Copy(i64));
+ configUpdate.push_back(std::move(str));
+
+ // Only example.i32 should be updated
+ ASSERT_TRUE(mConfig.updateConfiguration(configUpdate, D::IS_INPUT | D::IS_OUTPUT));
+
+ int32_t vendorInt32{0};
+ ASSERT_TRUE(mConfig.mInputFormat->findInt32(KEY_VENDOR_INT32, &vendorInt32))
+ << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str();
+ ASSERT_EQ(kCodec2Int32, vendorInt32);
+ ASSERT_FALSE(mConfig.mOutputFormat->findInt32(KEY_VENDOR_INT32, &vendorInt32))
+ << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+
+ int64_t vendorInt64{0};
+ ASSERT_FALSE(mConfig.mInputFormat->findInt64(KEY_VENDOR_INT64, &vendorInt64))
+ << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str();
+ ASSERT_FALSE(mConfig.mOutputFormat->findInt64(KEY_VENDOR_INT64, &vendorInt64))
+ << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+
+ AString vendorString;
+ ASSERT_FALSE(mConfig.mInputFormat->findString(KEY_VENDOR_STRING, &vendorString))
+ << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str();
+ ASSERT_FALSE(mConfig.mOutputFormat->findString(KEY_VENDOR_STRING, &vendorString))
+ << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+}
+
+} // namespace android
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
index 91d2eff..abc3239 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
@@ -72,7 +72,7 @@
// Implement FixedBlockProcessor
int32_t AudioStreamLegacy::onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) {
- int32_t numFrames = numBytes / getBytesPerDeviceFrame();
+ int32_t numFrames = numBytes / mBlockAdapterBytesPerFrame;
return (int32_t) callDataCallbackFrames(buffer, numFrames);
}
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.h b/media/libaaudio/src/legacy/AudioStreamLegacy.h
index 8e78554..da9205f 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.h
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.h
@@ -133,6 +133,7 @@
MonotonicCounter mTimestampPosition;
FixedBlockAdapter *mBlockAdapter = nullptr;
+ int32_t mBlockAdapterBytesPerFrame = 0;
aaudio_wrapping_frames_t mPositionWhenStarting = 0;
int32_t mCallbackBufferSize = 0;
const android::sp<StreamDeviceCallback> mDeviceCallback;
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index 2ed82d2..904edd1 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -213,7 +213,10 @@
// We may need to pass the data through a block size adapter to guarantee constant size.
if (mCallbackBufferSize != AAUDIO_UNSPECIFIED) {
- int callbackSizeBytes = getBytesPerFrame() * mCallbackBufferSize;
+ // The block adapter runs before the format conversion.
+ // So we need to use the device frame size.
+ mBlockAdapterBytesPerFrame = getBytesPerDeviceFrame();
+ int callbackSizeBytes = mBlockAdapterBytesPerFrame * mCallbackBufferSize;
mFixedBlockWriter.open(callbackSizeBytes);
mBlockAdapter = &mFixedBlockWriter;
} else {
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index 00963d6..d54d043 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -196,7 +196,10 @@
// We may need to pass the data through a block size adapter to guarantee constant size.
if (mCallbackBufferSize != AAUDIO_UNSPECIFIED) {
- int callbackSizeBytes = getBytesPerFrame() * mCallbackBufferSize;
+ // This may need to change if we add format conversion before
+ // the block size adaptation.
+ mBlockAdapterBytesPerFrame = getBytesPerFrame();
+ int callbackSizeBytes = mBlockAdapterBytesPerFrame * mCallbackBufferSize;
mFixedBlockReader.open(callbackSizeBytes);
mBlockAdapter = &mFixedBlockReader;
} else {
diff --git a/media/libaudioprocessing/Android.bp b/media/libaudioprocessing/Android.bp
index 1a6a5a2..39b0ceb 100644
--- a/media/libaudioprocessing/Android.bp
+++ b/media/libaudioprocessing/Android.bp
@@ -19,6 +19,25 @@
// uncomment to disable NEON on architectures that actually do support NEON, for benchmarking
// "-DUSE_NEON=false",
],
+
+ arch: {
+ x86: {
+ avx2: {
+ cflags: [
+ "-mavx2",
+ "-mfma",
+ ],
+ },
+ },
+ x86_64: {
+ avx2: {
+ cflags: [
+ "-mavx2",
+ "-mfma",
+ ],
+ },
+ },
+ },
}
cc_library_shared {
diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp
index c0b11a4..1a31420 100644
--- a/media/libaudioprocessing/AudioMixer.cpp
+++ b/media/libaudioprocessing/AudioMixer.cpp
@@ -162,10 +162,10 @@
// discard the previous downmixer if there was one
unprepareForDownmix();
// MONO_HACK Only remix (upmix or downmix) if the track and mixer/device channel masks
- // are not the same and not handled internally, as mono -> stereo currently is.
+ // are not the same and not handled internally, as mono for channel position masks is.
if (channelMask == mMixerChannelMask
|| (channelMask == AUDIO_CHANNEL_OUT_MONO
- && mMixerChannelMask == AUDIO_CHANNEL_OUT_STEREO)) {
+ && isAudioChannelPositionMask(mMixerChannelMask))) {
return NO_ERROR;
}
// DownmixerBufferProvider is only used for position masks.
diff --git a/media/libaudioprocessing/AudioMixerBase.cpp b/media/libaudioprocessing/AudioMixerBase.cpp
index 75c077d..64f91fe 100644
--- a/media/libaudioprocessing/AudioMixerBase.cpp
+++ b/media/libaudioprocessing/AudioMixerBase.cpp
@@ -643,14 +643,28 @@
if (n & NEEDS_RESAMPLE) {
all16BitsStereoNoResample = false;
resampling = true;
- t->hook = TrackBase::getTrackHook(TRACKTYPE_RESAMPLE, t->mMixerChannelCount,
- t->mMixerInFormat, t->mMixerFormat);
+ if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1
+ && t->channelMask == AUDIO_CHANNEL_OUT_MONO // MONO_HACK
+ && isAudioChannelPositionMask(t->mMixerChannelMask)) {
+ t->hook = TrackBase::getTrackHook(
+ TRACKTYPE_RESAMPLEMONO, t->mMixerChannelCount,
+ t->mMixerInFormat, t->mMixerFormat);
+ } else if ((n & NEEDS_CHANNEL_COUNT__MASK) >= NEEDS_CHANNEL_2
+ && t->useStereoVolume()) {
+ t->hook = TrackBase::getTrackHook(
+ TRACKTYPE_RESAMPLESTEREO, t->mMixerChannelCount,
+ t->mMixerInFormat, t->mMixerFormat);
+ } else {
+ t->hook = TrackBase::getTrackHook(
+ TRACKTYPE_RESAMPLE, t->mMixerChannelCount,
+ t->mMixerInFormat, t->mMixerFormat);
+ }
ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2,
"Track %d needs downmix + resample", name);
} else {
if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1){
t->hook = TrackBase::getTrackHook(
- (t->mMixerChannelMask == AUDIO_CHANNEL_OUT_STEREO // TODO: MONO_HACK
+ (isAudioChannelPositionMask(t->mMixerChannelMask) // TODO: MONO_HACK
&& t->channelMask == AUDIO_CHANNEL_OUT_MONO)
? TRACKTYPE_NORESAMPLEMONO : TRACKTYPE_NORESAMPLE,
t->mMixerChannelCount,
@@ -658,8 +672,11 @@
all16BitsStereoNoResample = false;
}
if ((n & NEEDS_CHANNEL_COUNT__MASK) >= NEEDS_CHANNEL_2){
- t->hook = TrackBase::getTrackHook(TRACKTYPE_NORESAMPLE, t->mMixerChannelCount,
- t->mMixerInFormat, t->mMixerFormat);
+ t->hook = TrackBase::getTrackHook(
+ t->useStereoVolume() ? TRACKTYPE_NORESAMPLESTEREO
+ : TRACKTYPE_NORESAMPLE,
+ t->mMixerChannelCount, t->mMixerInFormat,
+ t->mMixerFormat);
ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2,
"Track %d needs downmix", name);
}
@@ -691,7 +708,8 @@
// special case handling due to implicit channel duplication.
// Stereo or Multichannel should actually be fine here.
mHook = getProcessHook(PROCESSTYPE_NORESAMPLEONETRACK,
- t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat);
+ t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat,
+ t->useStereoVolume());
}
}
}
@@ -726,7 +744,8 @@
const std::shared_ptr<TrackBase> &t = mTracks[mEnabled[0]];
// Muted single tracks handled by allMuted above.
mHook = getProcessHook(PROCESSTYPE_NORESAMPLEONETRACK,
- t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat);
+ t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat,
+ t->useStereoVolume());
}
}
}
@@ -1450,7 +1469,7 @@
}
const size_t outFrames = b.frameCount;
- t->volumeMix<MIXTYPE, is_same<TI, float>::value /* USEFLOATVOL */, false /* ADJUSTVOL */> (
+ t->volumeMix<MIXTYPE, std::is_same_v<TI, float> /* USEFLOATVOL */, false /* ADJUSTVOL */> (
out, outFrames, in, aux, ramp);
out += outFrames * channels;
@@ -1463,7 +1482,7 @@
t->bufferProvider->releaseBuffer(&b);
}
if (ramp) {
- t->adjustVolumeRamp(aux != NULL, is_same<TI, float>::value);
+ t->adjustVolumeRamp(aux != NULL, std::is_same_v<TI, float>);
}
}
@@ -1481,7 +1500,8 @@
ALOGVV("track__Resample\n");
mResampler->setSampleRate(sampleRate);
const bool ramp = needsRamp();
- if (ramp || aux != NULL) {
+ if (MIXTYPE == MIXTYPE_MONOEXPAND || MIXTYPE == MIXTYPE_STEREOEXPAND
+ || ramp || aux != NULL) {
// if ramp: resample with unity gain to temp buffer and scale/mix in 2nd step.
// if aux != NULL: resample with unity gain to temp buffer then apply send level.
@@ -1489,7 +1509,7 @@
memset(temp, 0, outFrameCount * mMixerChannelCount * sizeof(TO));
mResampler->resample((int32_t*)temp, outFrameCount, bufferProvider);
- volumeMix<MIXTYPE, is_same<TI, float>::value /* USEFLOATVOL */, true /* ADJUSTVOL */>(
+ volumeMix<MIXTYPE, std::is_same_v<TI, float> /* USEFLOATVOL */, true /* ADJUSTVOL */>(
out, outFrameCount, temp, aux, ramp);
} else { // constant volume gain
@@ -1513,7 +1533,7 @@
ALOGVV("track__NoResample\n");
const TI *in = static_cast<const TI *>(mIn);
- volumeMix<MIXTYPE, is_same<TI, float>::value /* USEFLOATVOL */, true /* ADJUSTVOL */>(
+ volumeMix<MIXTYPE, std::is_same_v<TI, float> /* USEFLOATVOL */, true /* ADJUSTVOL */>(
out, frameCount, in, aux, needsRamp());
// MIXTYPE_MONOEXPAND reads a single input channel and expands to NCHAN output channels.
@@ -1601,6 +1621,38 @@
break;
}
break;
+ case TRACKTYPE_RESAMPLESTEREO:
+ switch (mixerInFormat) {
+ case AUDIO_FORMAT_PCM_FLOAT:
+ return (AudioMixerBase::hook_t) &TrackBase::track__Resample<
+ MIXTYPE_MULTI_STEREOVOL, float /*TO*/, float /*TI*/,
+ TYPE_AUX>;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ return (AudioMixerBase::hook_t) &TrackBase::track__Resample<
+ MIXTYPE_MULTI_STEREOVOL, int32_t /*TO*/, int16_t /*TI*/,
+ TYPE_AUX>;
+ default:
+ LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
+ break;
+ }
+ break;
+ // RESAMPLEMONO needs MIXTYPE_STEREOEXPAND since resampler will upmix mono
+ // track to stereo track
+ case TRACKTYPE_RESAMPLEMONO:
+ switch (mixerInFormat) {
+ case AUDIO_FORMAT_PCM_FLOAT:
+ return (AudioMixerBase::hook_t) &TrackBase::track__Resample<
+ MIXTYPE_STEREOEXPAND, float /*TO*/, float /*TI*/,
+ TYPE_AUX>;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ return (AudioMixerBase::hook_t) &TrackBase::track__Resample<
+ MIXTYPE_STEREOEXPAND, int32_t /*TO*/, int16_t /*TI*/,
+ TYPE_AUX>;
+ default:
+ LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
+ break;
+ }
+ break;
case TRACKTYPE_NORESAMPLEMONO:
switch (mixerInFormat) {
case AUDIO_FORMAT_PCM_FLOAT:
@@ -1627,6 +1679,21 @@
break;
}
break;
+ case TRACKTYPE_NORESAMPLESTEREO:
+ switch (mixerInFormat) {
+ case AUDIO_FORMAT_PCM_FLOAT:
+ return (AudioMixerBase::hook_t) &TrackBase::track__NoResample<
+ MIXTYPE_MULTI_STEREOVOL, float /*TO*/, float /*TI*/,
+ TYPE_AUX>;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ return (AudioMixerBase::hook_t) &TrackBase::track__NoResample<
+ MIXTYPE_MULTI_STEREOVOL, int32_t /*TO*/, int16_t /*TI*/,
+ TYPE_AUX>;
+ default:
+ LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
+ break;
+ }
+ break;
default:
LOG_ALWAYS_FATAL("bad trackType: %d", trackType);
break;
@@ -1644,7 +1711,8 @@
/* static */
AudioMixerBase::process_hook_t AudioMixerBase::getProcessHook(
int processType, uint32_t channelCount,
- audio_format_t mixerInFormat, audio_format_t mixerOutFormat)
+ audio_format_t mixerInFormat, audio_format_t mixerOutFormat,
+ bool stereoVolume)
{
if (processType != PROCESSTYPE_NORESAMPLEONETRACK) { // Only NORESAMPLEONETRACK
LOG_ALWAYS_FATAL("bad processType: %d", processType);
@@ -1654,36 +1722,79 @@
return &AudioMixerBase::process__oneTrack16BitsStereoNoResampling;
}
LOG_ALWAYS_FATAL_IF(channelCount > MAX_NUM_CHANNELS);
- switch (mixerInFormat) {
- case AUDIO_FORMAT_PCM_FLOAT:
- switch (mixerOutFormat) {
+
+ if (stereoVolume) { // templated arguments require explicit values.
+ switch (mixerInFormat) {
case AUDIO_FORMAT_PCM_FLOAT:
- return &AudioMixerBase::process__noResampleOneTrack<
- MIXTYPE_MULTI_SAVEONLY, float /*TO*/, float /*TI*/, TYPE_AUX>;
+ switch (mixerOutFormat) {
+ case AUDIO_FORMAT_PCM_FLOAT:
+ return &AudioMixerBase::process__noResampleOneTrack<
+ MIXTYPE_MULTI_SAVEONLY_STEREOVOL, float /*TO*/,
+ float /*TI*/, TYPE_AUX>;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ return &AudioMixerBase::process__noResampleOneTrack<
+ MIXTYPE_MULTI_SAVEONLY_STEREOVOL, int16_t /*TO*/,
+ float /*TI*/, TYPE_AUX>;
+ default:
+ LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
+ break;
+ }
+ break;
case AUDIO_FORMAT_PCM_16_BIT:
- return &AudioMixerBase::process__noResampleOneTrack<
- MIXTYPE_MULTI_SAVEONLY, int16_t /*TO*/, float /*TI*/, TYPE_AUX>;
+ switch (mixerOutFormat) {
+ case AUDIO_FORMAT_PCM_FLOAT:
+ return &AudioMixerBase::process__noResampleOneTrack<
+ MIXTYPE_MULTI_SAVEONLY_STEREOVOL, float /*TO*/,
+ int16_t /*TI*/, TYPE_AUX>;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ return &AudioMixerBase::process__noResampleOneTrack<
+ MIXTYPE_MULTI_SAVEONLY_STEREOVOL, int16_t /*TO*/,
+ int16_t /*TI*/, TYPE_AUX>;
+ default:
+ LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
+ break;
+ }
+ break;
default:
- LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
+ LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
break;
}
- break;
- case AUDIO_FORMAT_PCM_16_BIT:
- switch (mixerOutFormat) {
- case AUDIO_FORMAT_PCM_FLOAT:
- return &AudioMixerBase::process__noResampleOneTrack<
- MIXTYPE_MULTI_SAVEONLY, float /*TO*/, int16_t /*TI*/, TYPE_AUX>;
- case AUDIO_FORMAT_PCM_16_BIT:
- return &AudioMixerBase::process__noResampleOneTrack<
- MIXTYPE_MULTI_SAVEONLY, int16_t /*TO*/, int16_t /*TI*/, TYPE_AUX>;
- default:
- LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
- break;
- }
- break;
- default:
- LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
- break;
+ } else {
+ switch (mixerInFormat) {
+ case AUDIO_FORMAT_PCM_FLOAT:
+ switch (mixerOutFormat) {
+ case AUDIO_FORMAT_PCM_FLOAT:
+ return &AudioMixerBase::process__noResampleOneTrack<
+ MIXTYPE_MULTI_SAVEONLY, float /*TO*/,
+ float /*TI*/, TYPE_AUX>;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ return &AudioMixerBase::process__noResampleOneTrack<
+ MIXTYPE_MULTI_SAVEONLY, int16_t /*TO*/,
+ float /*TI*/, TYPE_AUX>;
+ default:
+ LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
+ break;
+ }
+ break;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ switch (mixerOutFormat) {
+ case AUDIO_FORMAT_PCM_FLOAT:
+ return &AudioMixerBase::process__noResampleOneTrack<
+ MIXTYPE_MULTI_SAVEONLY, float /*TO*/,
+ int16_t /*TI*/, TYPE_AUX>;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ return &AudioMixerBase::process__noResampleOneTrack<
+ MIXTYPE_MULTI_SAVEONLY, int16_t /*TO*/,
+ int16_t /*TI*/, TYPE_AUX>;
+ default:
+ LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
+ break;
+ }
+ break;
+ default:
+ LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
+ break;
+ }
}
return NULL;
}
diff --git a/media/libaudioprocessing/AudioMixerOps.h b/media/libaudioprocessing/AudioMixerOps.h
index f33e361..2748182 100644
--- a/media/libaudioprocessing/AudioMixerOps.h
+++ b/media/libaudioprocessing/AudioMixerOps.h
@@ -19,21 +19,10 @@
namespace android {
-/* Behavior of is_same<>::value is true if the types are identical,
- * false otherwise. Identical to the STL std::is_same.
- */
-template<typename T, typename U>
-struct is_same
-{
- static const bool value = false;
-};
-
-template<typename T>
-struct is_same<T, T> // partial specialization
-{
- static const bool value = true;
-};
-
+// Hack to make static_assert work in a constexpr
+// https://en.cppreference.com/w/cpp/language/if
+template <int N>
+inline constexpr bool dependent_false = false;
/* MixMul is a multiplication operator to scale an audio input signal
* by a volume gain, with the formula:
@@ -179,7 +168,7 @@
template <typename TO, typename TI>
inline void MixAccum(TO *auxaccum, TI value) {
- if (!is_same<TO, TI>::value) {
+ if (!std::is_same_v<TO, TI>) {
LOG_ALWAYS_FATAL("MixAccum type not properly specialized: %zu %zu\n",
sizeof(TO), sizeof(TI));
}
@@ -228,9 +217,79 @@
MIXTYPE_MULTI_SAVEONLY,
MIXTYPE_MULTI_MONOVOL,
MIXTYPE_MULTI_SAVEONLY_MONOVOL,
+ MIXTYPE_MULTI_STEREOVOL,
+ MIXTYPE_MULTI_SAVEONLY_STEREOVOL,
+ MIXTYPE_STEREOEXPAND,
};
/*
+ * TODO: We should work on non-interleaved streams - the
+ * complexity of working on interleaved streams is now getting
+ * too high, and likely limits compiler optimization.
+ */
+template <int MIXTYPE, int NCHAN,
+ typename TO, typename TI, typename TV,
+ typename F>
+void stereoVolumeHelper(TO*& out, const TI*& in, const TV *vol, F f) {
+ static_assert(NCHAN > 0 && NCHAN <= 8);
+ static_assert(MIXTYPE == MIXTYPE_MULTI_STEREOVOL
+ || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL
+ || MIXTYPE == MIXTYPE_STEREOEXPAND);
+ auto proc = [](auto& a, const auto& b) {
+ if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL) {
+ a += b;
+ } else {
+ a = b;
+ }
+ };
+ auto inp = [&in]() -> const TI& {
+ if constexpr (MIXTYPE == MIXTYPE_STEREOEXPAND) {
+ return *in;
+ } else {
+ return *in++;
+ }
+ };
+
+ // HALs should only expose the canonical channel masks.
+ proc(*out++, f(inp(), vol[0])); // front left
+ if constexpr (NCHAN == 1) return;
+ proc(*out++, f(inp(), vol[1])); // front right
+ if constexpr (NCHAN == 2) return;
+ if constexpr (NCHAN == 4) {
+ proc(*out++, f(inp(), vol[0])); // back left
+ proc(*out++, f(inp(), vol[1])); // back right
+ return;
+ }
+
+ // TODO: Precompute center volume if not ramping.
+ std::decay_t<TV> center;
+ if constexpr (std::is_floating_point_v<TV>) {
+ center = (vol[0] + vol[1]) * 0.5; // do not use divide
+ } else {
+ center = (vol[0] >> 1) + (vol[1] >> 1); // rounds to 0.
+ }
+ proc(*out++, f(inp(), center)); // center (or 2.1 LFE)
+ if constexpr (NCHAN == 3) return;
+ if constexpr (NCHAN == 5) {
+ proc(*out++, f(inp(), vol[0])); // back left
+ proc(*out++, f(inp(), vol[1])); // back right
+ return;
+ }
+
+ proc(*out++, f(inp(), center)); // lfe
+ proc(*out++, f(inp(), vol[0])); // back left
+ proc(*out++, f(inp(), vol[1])); // back right
+ if constexpr (NCHAN == 6) return;
+ if constexpr (NCHAN == 7) {
+ proc(*out++, f(inp(), center)); // back center
+ return;
+ }
+ // NCHAN == 8
+ proc(*out++, f(inp(), vol[0])); // side left
+ proc(*out++, f(inp(), vol[1])); // side right
+}
+
+/*
* The volumeRampMulti and volumeRamp functions take a MIXTYPE
* which indicates the per-frame mixing and accumulation strategy.
*
@@ -271,6 +330,17 @@
* MIXTYPE_MULTI_SAVEONLY_MONOVOL:
* Same as MIXTYPE_MULTI_SAVEONLY, but uses only volume[0].
*
+ * MIXTYPE_MULTI_STEREOVOL:
+ * Same as MIXTYPE_MULTI, but uses only volume[0] and volume[1].
+ *
+ * MIXTYPE_MULTI_SAVEONLY_STEREOVOL:
+ * Same as MIXTYPE_MULTI_SAVEONLY, but uses only volume[0] and volume[1].
+ *
+ * MIXTYPE_STEREOEXPAND:
+ * Stereo input channel. NCHAN represents number of output channels.
+ * Expand size 2 array "in" and "vol" to multi-channel output. Note
+ * that the 2 array is assumed to have replicated L+R.
+ *
*/
template <int MIXTYPE, int NCHAN,
@@ -284,41 +354,44 @@
if (aux != NULL) {
do {
TA auxaccum = 0;
- switch (MIXTYPE) {
- case MIXTYPE_MULTI:
+ if constexpr (MIXTYPE == MIXTYPE_MULTI) {
for (int i = 0; i < NCHAN; ++i) {
*out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
vol[i] += volinc[i];
}
- break;
- case MIXTYPE_MONOEXPAND:
+ } else if constexpr (MIXTYPE == MIXTYPE_MONOEXPAND) {
for (int i = 0; i < NCHAN; ++i) {
*out++ += MixMulAux<TO, TI, TV, TA>(*in, vol[i], &auxaccum);
vol[i] += volinc[i];
}
in++;
- break;
- case MIXTYPE_MULTI_SAVEONLY:
+ } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY) {
for (int i = 0; i < NCHAN; ++i) {
*out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
vol[i] += volinc[i];
}
- break;
- case MIXTYPE_MULTI_MONOVOL:
+ } else if constexpr (MIXTYPE == MIXTYPE_MULTI_MONOVOL) {
for (int i = 0; i < NCHAN; ++i) {
*out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
}
vol[0] += volinc[0];
- break;
- case MIXTYPE_MULTI_SAVEONLY_MONOVOL:
+ } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY_MONOVOL) {
for (int i = 0; i < NCHAN; ++i) {
*out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
}
vol[0] += volinc[0];
- break;
- default:
- LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE);
- break;
+ } else if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
+ || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL
+ || MIXTYPE == MIXTYPE_STEREOEXPAND) {
+ stereoVolumeHelper<MIXTYPE, NCHAN>(
+ out, in, vol, [&auxaccum] (auto &a, const auto &b) {
+ return MixMulAux<TO, TI, TV, TA>(a, b, &auxaccum);
+ });
+ if constexpr (MIXTYPE == MIXTYPE_STEREOEXPAND) in += 2;
+ vol[0] += volinc[0];
+ vol[1] += volinc[1];
+ } else /* constexpr */ {
+ static_assert(dependent_false<MIXTYPE>, "invalid mixtype");
}
auxaccum /= NCHAN;
*aux++ += MixMul<TA, TA, TAV>(auxaccum, *vola);
@@ -326,41 +399,43 @@
} while (--frameCount);
} else {
do {
- switch (MIXTYPE) {
- case MIXTYPE_MULTI:
+ if constexpr (MIXTYPE == MIXTYPE_MULTI) {
for (int i = 0; i < NCHAN; ++i) {
*out++ += MixMul<TO, TI, TV>(*in++, vol[i]);
vol[i] += volinc[i];
}
- break;
- case MIXTYPE_MONOEXPAND:
+ } else if constexpr (MIXTYPE == MIXTYPE_MONOEXPAND) {
for (int i = 0; i < NCHAN; ++i) {
*out++ += MixMul<TO, TI, TV>(*in, vol[i]);
vol[i] += volinc[i];
}
in++;
- break;
- case MIXTYPE_MULTI_SAVEONLY:
+ } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY) {
for (int i = 0; i < NCHAN; ++i) {
*out++ = MixMul<TO, TI, TV>(*in++, vol[i]);
vol[i] += volinc[i];
}
- break;
- case MIXTYPE_MULTI_MONOVOL:
+ } else if constexpr (MIXTYPE == MIXTYPE_MULTI_MONOVOL) {
for (int i = 0; i < NCHAN; ++i) {
*out++ += MixMul<TO, TI, TV>(*in++, vol[0]);
}
vol[0] += volinc[0];
- break;
- case MIXTYPE_MULTI_SAVEONLY_MONOVOL:
+ } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY_MONOVOL) {
for (int i = 0; i < NCHAN; ++i) {
*out++ = MixMul<TO, TI, TV>(*in++, vol[0]);
}
vol[0] += volinc[0];
- break;
- default:
- LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE);
- break;
+ } else if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
+ || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL
+ || MIXTYPE == MIXTYPE_STEREOEXPAND) {
+ stereoVolumeHelper<MIXTYPE, NCHAN>(out, in, vol, [] (auto &a, const auto &b) {
+ return MixMul<TO, TI, TV>(a, b);
+ });
+ if constexpr (MIXTYPE == MIXTYPE_STEREOEXPAND) in += 2;
+ vol[0] += volinc[0];
+ vol[1] += volinc[1];
+ } else /* constexpr */ {
+ static_assert(dependent_false<MIXTYPE>, "invalid mixtype");
}
} while (--frameCount);
}
@@ -377,72 +452,73 @@
if (aux != NULL) {
do {
TA auxaccum = 0;
- switch (MIXTYPE) {
- case MIXTYPE_MULTI:
+ if constexpr (MIXTYPE == MIXTYPE_MULTI) {
for (int i = 0; i < NCHAN; ++i) {
*out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
}
- break;
- case MIXTYPE_MONOEXPAND:
+ } else if constexpr (MIXTYPE == MIXTYPE_MONOEXPAND) {
for (int i = 0; i < NCHAN; ++i) {
*out++ += MixMulAux<TO, TI, TV, TA>(*in, vol[i], &auxaccum);
}
in++;
- break;
- case MIXTYPE_MULTI_SAVEONLY:
+ } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY) {
for (int i = 0; i < NCHAN; ++i) {
*out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
}
- break;
- case MIXTYPE_MULTI_MONOVOL:
+ } else if constexpr (MIXTYPE == MIXTYPE_MULTI_MONOVOL) {
for (int i = 0; i < NCHAN; ++i) {
*out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
}
- break;
- case MIXTYPE_MULTI_SAVEONLY_MONOVOL:
+ } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY_MONOVOL) {
for (int i = 0; i < NCHAN; ++i) {
*out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
}
- break;
- default:
- LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE);
- break;
+ } else if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
+ || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL
+ || MIXTYPE == MIXTYPE_STEREOEXPAND) {
+ stereoVolumeHelper<MIXTYPE, NCHAN>(
+ out, in, vol, [&auxaccum] (auto &a, const auto &b) {
+ return MixMulAux<TO, TI, TV, TA>(a, b, &auxaccum);
+ });
+ if constexpr (MIXTYPE == MIXTYPE_STEREOEXPAND) in += 2;
+ } else /* constexpr */ {
+ static_assert(dependent_false<MIXTYPE>, "invalid mixtype");
}
auxaccum /= NCHAN;
*aux++ += MixMul<TA, TA, TAV>(auxaccum, vola);
} while (--frameCount);
} else {
do {
- switch (MIXTYPE) {
- case MIXTYPE_MULTI:
+ if constexpr (MIXTYPE == MIXTYPE_MULTI) {
for (int i = 0; i < NCHAN; ++i) {
*out++ += MixMul<TO, TI, TV>(*in++, vol[i]);
}
- break;
- case MIXTYPE_MONOEXPAND:
+ } else if constexpr (MIXTYPE == MIXTYPE_MONOEXPAND) {
for (int i = 0; i < NCHAN; ++i) {
*out++ += MixMul<TO, TI, TV>(*in, vol[i]);
}
in++;
- break;
- case MIXTYPE_MULTI_SAVEONLY:
+ } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY) {
for (int i = 0; i < NCHAN; ++i) {
*out++ = MixMul<TO, TI, TV>(*in++, vol[i]);
}
- break;
- case MIXTYPE_MULTI_MONOVOL:
+ } else if constexpr (MIXTYPE == MIXTYPE_MULTI_MONOVOL) {
for (int i = 0; i < NCHAN; ++i) {
*out++ += MixMul<TO, TI, TV>(*in++, vol[0]);
}
- break;
- case MIXTYPE_MULTI_SAVEONLY_MONOVOL:
+ } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY_MONOVOL) {
for (int i = 0; i < NCHAN; ++i) {
*out++ = MixMul<TO, TI, TV>(*in++, vol[0]);
}
- break;
- default:
- LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE);
- break;
+ } else if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
+ || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL
+ || MIXTYPE == MIXTYPE_STEREOEXPAND) {
+ stereoVolumeHelper<MIXTYPE, NCHAN>(out, in, vol, [] (auto &a, const auto &b) {
+ return MixMul<TO, TI, TV>(a, b);
+ });
+ if constexpr (MIXTYPE == MIXTYPE_STEREOEXPAND) in += 2;
+ } else /* constexpr */ {
+ static_assert(dependent_false<MIXTYPE>, "invalid mixtype");
}
} while (--frameCount);
}
diff --git a/media/libaudioprocessing/AudioResamplerFirOps.h b/media/libaudioprocessing/AudioResamplerFirOps.h
index 2e4cee3..a3f5ff5 100644
--- a/media/libaudioprocessing/AudioResamplerFirOps.h
+++ b/media/libaudioprocessing/AudioResamplerFirOps.h
@@ -36,13 +36,20 @@
#include <arm_neon.h>
#endif
-#if defined(__SSSE3__) // Should be supported in x86 ABI for both 32 & 64-bit.
+#if defined(__AVX2__) // Should be supported in x86 ABI for both 32 & 64-bit.
+#define USE_AVX2 (true) // Inference AVX2/FMA Intrinsics
#define USE_SSE (true)
+#include <immintrin.h>
+#elif defined(__SSSE3__) // Should be supported in x86 ABI for both 32 & 64-bit.
+#define USE_SSE (true) // Inference SSE Intrinsics
+#define USE_AVX2 (false)
#include <tmmintrin.h>
#else
#define USE_SSE (false)
+#define USE_AVX2(false)
#endif
+
template<typename T, typename U>
struct is_same
{
diff --git a/media/libaudioprocessing/AudioResamplerFirProcessSSE.h b/media/libaudioprocessing/AudioResamplerFirProcessSSE.h
index 30233b5..1c16bc4 100644
--- a/media/libaudioprocessing/AudioResamplerFirProcessSSE.h
+++ b/media/libaudioprocessing/AudioResamplerFirProcessSSE.h
@@ -80,11 +80,16 @@
posCoef1 = _mm_sub_ps(posCoef1, posCoef);
negCoef = _mm_sub_ps(negCoef, negCoef1);
+
+ #if USE_AVX2
+ posCoef = _mm_fmadd_ps(posCoef1, interp, posCoef);
+ negCoef = _mm_fmadd_ps(negCoef, interp, negCoef1);
+ #else
posCoef1 = _mm_mul_ps(posCoef1, interp);
negCoef = _mm_mul_ps(negCoef, interp);
-
posCoef = _mm_add_ps(posCoef1, posCoef);
negCoef = _mm_add_ps(negCoef, negCoef1);
+ #endif //USE_AVX2
}
switch (CHANNELS) {
case 1: {
@@ -94,11 +99,17 @@
sN += 4;
posSamp = _mm_shuffle_ps(posSamp, posSamp, 0x1B);
+
+ #if USE_AVX2
+ accL = _mm_fmadd_ps(posSamp, posCoef, accL);
+ accL = _mm_fmadd_ps(negSamp, negCoef, accL);
+ #else
posSamp = _mm_mul_ps(posSamp, posCoef);
negSamp = _mm_mul_ps(negSamp, negCoef);
-
accL = _mm_add_ps(accL, posSamp);
accL = _mm_add_ps(accL, negSamp);
+ #endif
+
} break;
case 2: {
__m128 posSamp0 = _mm_loadu_ps(sP);
@@ -114,15 +125,23 @@
__m128 negSampL = _mm_shuffle_ps(negSamp0, negSamp1, 0x88);
__m128 negSampR = _mm_shuffle_ps(negSamp0, negSamp1, 0xDD);
- posSampL = _mm_mul_ps(posSampL, posCoef);
- posSampR = _mm_mul_ps(posSampR, posCoef);
- negSampL = _mm_mul_ps(negSampL, negCoef);
- negSampR = _mm_mul_ps(negSampR, negCoef);
+ #if USE_AVX2
+ accL = _mm_fmadd_ps(posSampL, posCoef, accL);
+ accR = _mm_fmadd_ps(posSampR, posCoef, accR);
+ accL = _mm_fmadd_ps(negSampL, negCoef, accL);
+ accR = _mm_fmadd_ps(negSampR, negCoef, accR);
+ #else
+ posSampL = _mm_mul_ps(posSampL, posCoef);
+ posSampR = _mm_mul_ps(posSampR, posCoef);
+ negSampL = _mm_mul_ps(negSampL, negCoef);
+ negSampR = _mm_mul_ps(negSampR, negCoef);
- accL = _mm_add_ps(accL, posSampL);
- accR = _mm_add_ps(accR, posSampR);
- accL = _mm_add_ps(accL, negSampL);
- accR = _mm_add_ps(accR, negSampR);
+ accL = _mm_add_ps(accL, posSampL);
+ accR = _mm_add_ps(accR, posSampR);
+ accL = _mm_add_ps(accL, negSampL);
+ accR = _mm_add_ps(accR, negSampR);
+ #endif
+
} break;
}
} while (count -= 4);
@@ -144,9 +163,13 @@
outAccum = _mm_hadd_ps(accL, accR);
outAccum = _mm_hadd_ps(outAccum, outAccum);
}
-
+ #if USE_AVX2
+ outSamp = _mm_fmadd_ps(outAccum, vLR,outSamp);
+ #else
outAccum = _mm_mul_ps(outAccum, vLR);
outSamp = _mm_add_ps(outSamp, outAccum);
+ #endif
+
_mm_storel_pi(reinterpret_cast<__m64*>(out), outSamp);
}
diff --git a/media/libaudioprocessing/include/media/AudioMixerBase.h b/media/libaudioprocessing/include/media/AudioMixerBase.h
index 805b6d0..cf84b83 100644
--- a/media/libaudioprocessing/include/media/AudioMixerBase.h
+++ b/media/libaudioprocessing/include/media/AudioMixerBase.h
@@ -188,13 +188,21 @@
enum {
TRACKTYPE_NOP,
TRACKTYPE_RESAMPLE,
+ TRACKTYPE_RESAMPLEMONO,
+ TRACKTYPE_RESAMPLESTEREO,
TRACKTYPE_NORESAMPLE,
TRACKTYPE_NORESAMPLEMONO,
+ TRACKTYPE_NORESAMPLESTEREO,
};
// process hook functionality
using process_hook_t = void(AudioMixerBase::*)();
+ static bool isAudioChannelPositionMask(audio_channel_mask_t channelMask) {
+ return audio_channel_mask_get_representation(channelMask)
+ == AUDIO_CHANNEL_REPRESENTATION_POSITION;
+ }
+
struct TrackBase;
using hook_t = void(TrackBase::*)(
int32_t* output, size_t numOutFrames, int32_t* temp, int32_t* aux);
@@ -219,6 +227,9 @@
size_t getUnreleasedFrames() const { return mResampler.get() != nullptr ?
mResampler->getUnreleasedFrames() : 0; };
+ bool useStereoVolume() const { return channelMask == AUDIO_CHANNEL_OUT_STEREO
+ && isAudioChannelPositionMask(mMixerChannelMask); }
+
static hook_t getTrackHook(int trackType, uint32_t channelCount,
audio_format_t mixerInFormat, audio_format_t mixerOutFormat);
@@ -327,7 +338,8 @@
void process__noResampleOneTrack();
static process_hook_t getProcessHook(int processType, uint32_t channelCount,
- audio_format_t mixerInFormat, audio_format_t mixerOutFormat);
+ audio_format_t mixerInFormat, audio_format_t mixerOutFormat,
+ bool useStereoVolume);
static void convertMixerFormat(void *out, audio_format_t mixerOutFormat,
void *in, audio_format_t mixerInFormat, size_t sampleCount);
diff --git a/media/libaudioprocessing/tests/Android.bp b/media/libaudioprocessing/tests/Android.bp
index 20c2c2c..18acef7 100644
--- a/media/libaudioprocessing/tests/Android.bp
+++ b/media/libaudioprocessing/tests/Android.bp
@@ -55,3 +55,26 @@
srcs: ["test-resampler.cpp"],
static_libs: ["libsndfile"],
}
+
+//
+// build mixerops objdump
+//
+// This is used to verify proper optimization of the code.
+//
+// For example, use:
+// ./prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin/aarch64-linux-android-objdump
+// -d --source ./out/target/product/crosshatch/symbols/system/bin/mixerops_objdump
+//
+cc_binary {
+ name: "mixerops_objdump",
+ srcs: ["mixerops_objdump.cpp"],
+}
+
+//
+// build mixerops benchmark
+//
+cc_benchmark {
+ name: "mixerops_benchmark",
+ srcs: ["mixerops_benchmark.cpp"],
+ static_libs: ["libgoogle-benchmark"],
+}
diff --git a/media/libaudioprocessing/tests/mixerops_benchmark.cpp b/media/libaudioprocessing/tests/mixerops_benchmark.cpp
new file mode 100644
index 0000000..86f5429
--- /dev/null
+++ b/media/libaudioprocessing/tests/mixerops_benchmark.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <type_traits>
+#include "../../../../system/media/audio_utils/include/audio_utils/primitives.h"
+#define LOG_ALWAYS_FATAL(...)
+
+#include <../AudioMixerOps.h>
+
+#include <benchmark/benchmark.h>
+
+using namespace android;
+
+template <int MIXTYPE, int NCHAN>
+static void BM_VolumeRampMulti(benchmark::State& state) {
+ constexpr size_t FRAME_COUNT = 1000;
+ constexpr size_t SAMPLE_COUNT = FRAME_COUNT * NCHAN;
+
+ // data inialized to 0.
+ float out[SAMPLE_COUNT]{};
+ float in[SAMPLE_COUNT]{};
+ float aux[FRAME_COUNT]{};
+
+ // volume initialized to 0
+ float vola = 0.f;
+ float vol[2] = {0.f, 0.f};
+
+ // some volume increment
+ float volainc = 0.01f;
+ float volinc[2] = {0.01f, 0.01f};
+
+ while (state.KeepRunning()) {
+ benchmark::DoNotOptimize(out);
+ benchmark::DoNotOptimize(in);
+ volumeRampMulti<MIXTYPE, NCHAN>(out, FRAME_COUNT, in, aux, vol, volinc, &vola, volainc);
+ benchmark::ClobberMemory();
+ }
+}
+
+template <int MIXTYPE, int NCHAN>
+static void BM_VolumeMulti(benchmark::State& state) {
+ constexpr size_t FRAME_COUNT = 1000;
+ constexpr size_t SAMPLE_COUNT = FRAME_COUNT * NCHAN;
+
+ // data inialized to 0.
+ float out[SAMPLE_COUNT]{};
+ float in[SAMPLE_COUNT]{};
+ float aux[FRAME_COUNT]{};
+
+ // volume initialized to 0
+ float vola = 0.f;
+ float vol[2] = {0.f, 0.f};
+
+
+ while (state.KeepRunning()) {
+ benchmark::DoNotOptimize(out);
+ benchmark::DoNotOptimize(in);
+ volumeMulti<MIXTYPE, NCHAN>(out, FRAME_COUNT, in, aux, vol, vola);
+ benchmark::ClobberMemory();
+ }
+}
+
+BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI, 2);
+BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_SAVEONLY, 2);
+BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_STEREOVOL, 2);
+BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_SAVEONLY_STEREOVOL, 2);
+
+BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI, 4);
+BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_SAVEONLY, 4);
+BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_STEREOVOL, 4);
+BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_SAVEONLY_STEREOVOL, 4);
+
+BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI, 5);
+BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_SAVEONLY, 5);
+BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_STEREOVOL, 5);
+BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_SAVEONLY_STEREOVOL, 5);
+
+BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI, 8);
+BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_SAVEONLY, 8);
+BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_STEREOVOL, 8);
+BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_SAVEONLY_STEREOVOL, 8);
+
+BENCHMARK_TEMPLATE(BM_VolumeMulti, MIXTYPE_MULTI, 8);
+BENCHMARK_TEMPLATE(BM_VolumeMulti, MIXTYPE_MULTI_SAVEONLY, 8);
+BENCHMARK_TEMPLATE(BM_VolumeMulti, MIXTYPE_MULTI_STEREOVOL, 8);
+BENCHMARK_TEMPLATE(BM_VolumeMulti, MIXTYPE_MULTI_SAVEONLY_STEREOVOL, 8);
+
+BENCHMARK_MAIN();
diff --git a/media/libaudioprocessing/tests/mixerops_objdump.cpp b/media/libaudioprocessing/tests/mixerops_objdump.cpp
new file mode 100644
index 0000000..ee7e837
--- /dev/null
+++ b/media/libaudioprocessing/tests/mixerops_objdump.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <type_traits>
+#include "../../../../system/media/audio_utils/include/audio_utils/primitives.h"
+#define LOG_ALWAYS_FATAL(...)
+
+#include <../AudioMixerOps.h>
+
+using namespace android;
+
+template <int MIXTYPE, int NCHAN>
+static void checkVolumeRampMulti() {
+ constexpr size_t FRAME_COUNT = 1000;
+ constexpr size_t SAMPLE_COUNT = FRAME_COUNT * NCHAN;
+
+ // data inialized to 0.
+ float out[SAMPLE_COUNT]{};
+ float in[SAMPLE_COUNT]{};
+ float aux[FRAME_COUNT]{};
+
+ // volume initialized to 0
+ float vola = 0.f;
+ float vol[2] = {0.f, 0.f};
+
+ // some volume increment
+ float volainc = 0.01f;
+ float volinc[2] = {0.01f, 0.01f};
+
+ // try the multi ramp code.
+ volumeRampMulti<MIXTYPE, NCHAN>(out, FRAME_COUNT, in, aux, vol, volinc, &vola, volainc);
+}
+
+// Use this to check the objdump to ensure reasonable code.
+int main() {
+ checkVolumeRampMulti<MIXTYPE_MULTI_STEREOVOL, 5>();
+ return EXIT_SUCCESS;
+}
diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp
index 44dbfe9..94267a1 100644
--- a/media/libstagefright/MediaExtractorFactory.cpp
+++ b/media/libstagefright/MediaExtractorFactory.cpp
@@ -280,7 +280,7 @@
std::shared_ptr<std::list<sp<ExtractorPlugin>>> newList(new std::list<sp<ExtractorPlugin>>());
- android_namespace_t *mediaNs = android_get_exported_namespace("com.android.media");
+ android_namespace_t *mediaNs = android_get_exported_namespace("com_android_media");
if (mediaNs != NULL) {
const android_dlextinfo dlextinfo = {
.flags = ANDROID_DLEXT_USE_NAMESPACE,
diff --git a/media/libstagefright/TEST_MAPPING b/media/libstagefright/TEST_MAPPING
index fac9511..8b36ea5 100644
--- a/media/libstagefright/TEST_MAPPING
+++ b/media/libstagefright/TEST_MAPPING
@@ -1,7 +1,6 @@
{
- "postsubmit": [
+ "presubmit": [
{
- // TODO: move to presubmit once we verify the tests are not flaky
"name": "CtsMediaTestCases",
"options": [
{
@@ -13,9 +12,14 @@
// TODO: b/149314419
{
"exclude-filter": "android.media.cts.AudioPlaybackCaptureTest"
+ },
+ {
+ "exclude-filter": "android.media.cts.AudioRecordTest"
}
]
- },
+ }
+ ],
+ "postsubmit": [
{
"name": "BatteryChecker_test"
}
diff --git a/media/libstagefright/codecs/m4v_h263/enc/test/Android.bp b/media/libstagefright/codecs/m4v_h263/enc/test/Android.bp
new file mode 100644
index 0000000..b9a8117
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/enc/test/Android.bp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_test {
+ name: "Mpeg4H263EncoderTest",
+ gtest: true,
+
+ srcs : [ "Mpeg4H263EncoderTest.cpp" ],
+
+ shared_libs: [
+ "libutils",
+ "liblog",
+ ],
+
+ static_libs: [
+ "libstagefright_m4vh263enc",
+ ],
+
+ cflags: [
+ "-DOSCL_IMPORT_REF=",
+ "-Wall",
+ "-Werror",
+ ],
+
+ sanitize: {
+ misc_undefined: [
+ "signed-integer-overflow",
+ "unsigned-integer-overflow",
+ ],
+ cfi: true,
+ },
+}
diff --git a/media/libstagefright/codecs/m4v_h263/enc/test/AndroidTest.xml b/media/libstagefright/codecs/m4v_h263/enc/test/AndroidTest.xml
new file mode 100644
index 0000000..5218932
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/enc/test/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Test module config for MPEG4H263 encoder unit tests">
+ <option name="test-suite-tag" value="Mpeg4H263EncoderTest" />
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="Mpeg4H263EncoderTest->/data/local/tmp/Mpeg4H263EncoderTest/" />
+ <option name="push-file"
+ key="https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/codecs/m4v_h263/enc/test/Mpeg4H263Encoder.zip?unzip=true"
+ value="/data/local/tmp/Mpeg4H263EncoderTestRes/" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="Mpeg4H263EncoderTest" />
+ <option name="native-test-flag" value="-P /data/local/tmp/Mpeg4H263EncoderTestRes/" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/media/libstagefright/codecs/m4v_h263/enc/test/Mpeg4H263EncoderTest.cpp b/media/libstagefright/codecs/m4v_h263/enc/test/Mpeg4H263EncoderTest.cpp
new file mode 100644
index 0000000..78c705a
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/enc/test/Mpeg4H263EncoderTest.cpp
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Mpeg4H263EncoderTest"
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include "mp4enc_api.h"
+
+#include "Mpeg4H263EncoderTestEnvironment.h"
+
+#define ENCODED_FILE "/data/local/tmp/Mpeg4H263Output"
+
+// assuming a worst case compression of 2X
+constexpr int16_t kCompressionRatio = 2;
+constexpr int8_t kIDRFrameRefreshIntervalInSec = 1;
+
+static Mpeg4H263EncoderTestEnvironment *gEnv = nullptr;
+
+class Mpeg4H263EncoderTest
+ : public ::testing::TestWithParam<tuple<string, bool, int32_t, int32_t, float, int32_t>> {
+ private:
+ void initEncoderParams();
+
+ public:
+ Mpeg4H263EncoderTest()
+ : mInputBuffer(nullptr),
+ mOutputBuffer(nullptr),
+ mFpInput(nullptr),
+ mFpOutput(nullptr),
+ mEncodeHandle(nullptr),
+ mEncodeControl(nullptr) {}
+
+ ~Mpeg4H263EncoderTest() {
+ if(mFpInput) {
+ fclose(mFpInput);
+ }
+ if(mFpOutput) {
+ fclose(mFpOutput);
+ }
+ if(mInputBuffer) free(mInputBuffer);
+ if(mOutputBuffer) free(mOutputBuffer);
+ if(mEncodeHandle) free(mEncodeHandle);
+ if(mEncodeControl) free(mEncodeControl);
+ }
+
+ void SetUp() override {
+ tuple<string /* fileName */, bool /* isMpeg4 */, int32_t /* frameWidth */,
+ int32_t /* frameHeight */, float /* frameRate */, int32_t /* bitRate */>
+ params = GetParam();
+ mFileName = gEnv->getRes() + get<0>(params);
+ mIsMpeg4 = get<1>(params);
+ mFrameWidth = get<2>(params);
+ mFrameHeight = get<3>(params);
+ mFrameRate = get<4>(params);
+ mBitRate = get<5>(params);
+
+ ASSERT_TRUE(mFrameWidth % 16 == 0) << "Frame Width should be multiple of 16";
+ ASSERT_TRUE(mFrameHeight % 16 == 0) << "Frame Height should be multiple of 16";
+ ASSERT_LE(mFrameWidth, (mIsMpeg4 ? 720 : 352))
+ << "Frame Width <= 720 for Mpeg4 and <= 352 for H263";
+ ASSERT_LE(mFrameHeight, (mIsMpeg4 ? 480 : 288))
+ << "Frame Height <= 480 for Mpeg4 and <= 288 for H263";
+ ASSERT_LE(mFrameRate, 30) << "Frame rate less than or equal to 30";
+ ASSERT_LE(mBitRate, 2048) << "Bit rate less than or equal to 2048 kbps";
+
+ mOutputBufferSize = ( mFrameWidth * mFrameHeight * 3/2 ) / kCompressionRatio;
+ mEncodeHandle = new VideoEncOptions;
+ ASSERT_NE(mEncodeHandle, nullptr) << "Failed to get Video Encoding options object";
+ memset(mEncodeHandle, 0, sizeof(VideoEncOptions));
+ mEncodeControl = new VideoEncControls;
+ ASSERT_NE(mEncodeControl, nullptr) << "Failed to get Video Encoding control object";
+ memset(mEncodeControl, 0, sizeof(VideoEncControls));
+ ASSERT_NO_FATAL_FAILURE(initEncoderParams())
+ << "Failed to get the default Encoding parameters!";
+ }
+
+ int64_t getTotalFrames();
+ void processEncoder(int32_t);
+ bool mIsMpeg4;
+ int32_t mFrameWidth, mFrameHeight, mBitRate;
+ int64_t mOutputBufferSize;
+ float mFrameRate;
+ string mFileName;
+ uint8_t *mInputBuffer, *mOutputBuffer;
+ FILE *mFpInput, *mFpOutput;
+ VideoEncOptions *mEncodeHandle;
+ VideoEncControls *mEncodeControl;
+};
+
+void Mpeg4H263EncoderTest::initEncoderParams() {
+ bool status = PVGetDefaultEncOption(mEncodeHandle, 0);
+ ASSERT_TRUE(status);
+
+ mEncodeHandle->rcType = VBR_1;
+ mEncodeHandle->vbvDelay = 5.0f;
+ mEncodeHandle->profile_level = CORE_PROFILE_LEVEL2;
+ mEncodeHandle->packetSize = 32;
+ mEncodeHandle->rvlcEnable = PV_OFF;
+ mEncodeHandle->numLayers = 1;
+ mEncodeHandle->timeIncRes = 1000;
+ mEncodeHandle->iQuant[0] = 15;
+ mEncodeHandle->pQuant[0] = 12;
+ mEncodeHandle->quantType[0] = 0;
+ mEncodeHandle->noFrameSkipped = PV_OFF;
+ mEncodeHandle->numIntraMB = 0;
+ mEncodeHandle->sceneDetect = PV_ON;
+ mEncodeHandle->searchRange = 16;
+ mEncodeHandle->mv8x8Enable = PV_OFF;
+ mEncodeHandle->gobHeaderInterval = 0;
+ mEncodeHandle->useACPred = PV_ON;
+ mEncodeHandle->intraDCVlcTh = 0;
+ if(!mIsMpeg4) {
+ mEncodeHandle->encMode = H263_MODE;
+ } else {
+ mEncodeHandle->encMode = COMBINE_MODE_WITH_ERR_RES;
+ }
+ mEncodeHandle->encWidth[0] = mFrameWidth;
+ mEncodeHandle->encHeight[0] = mFrameHeight;
+ mEncodeHandle->encFrameRate[0] = mFrameRate;
+ mEncodeHandle->bitRate[0] = mBitRate * 1024;
+ mEncodeHandle->tickPerSrc = mEncodeHandle->timeIncRes / mFrameRate;
+ if (kIDRFrameRefreshIntervalInSec == 0) {
+ // All I frames.
+ mEncodeHandle->intraPeriod = 1;
+ } else {
+ mEncodeHandle->intraPeriod = (kIDRFrameRefreshIntervalInSec * mFrameRate);
+ }
+}
+
+int64_t Mpeg4H263EncoderTest::getTotalFrames() {
+ int32_t frameSize = (mFrameWidth * mFrameHeight * 3) / 2;
+ struct stat buf;
+ stat(mFileName.c_str(), &buf);
+ size_t fileSize = buf.st_size;
+ int64_t totalFrames = (int64_t)(fileSize/frameSize);
+ return totalFrames;
+}
+
+void Mpeg4H263EncoderTest::processEncoder(int32_t numFramesToEncode) {
+ bool status;
+ int64_t numEncodedFrames = 0;
+ int32_t bytesRead;
+ int32_t frameSize = (mFrameWidth * mFrameHeight * 3) / 2;
+ while(numFramesToEncode != 0) {
+ bytesRead = fread(mInputBuffer, 1, frameSize, mFpInput);
+ // End of file.
+ if (bytesRead != frameSize) {
+ break;
+ }
+
+ VideoEncFrameIO videoIn, videoOut;
+ videoIn.height = mFrameHeight;
+ videoIn.pitch = mFrameWidth;
+ videoIn.timestamp = (numEncodedFrames * 1000) / mFrameRate; // in ms.
+ videoIn.yChan = mInputBuffer;
+ videoIn.uChan = videoIn.yChan + videoIn.height * videoIn.pitch;
+ videoIn.vChan = videoIn.uChan + ((videoIn.height * videoIn.pitch) >> 2);
+ uint32_t modTimeMs = 0;
+ int32_t dataLength = mOutputBufferSize;
+ int32_t nLayer = 0;
+ status = PVEncodeVideoFrame(mEncodeControl, &videoIn, &videoOut, &modTimeMs, mOutputBuffer,
+ &dataLength, &nLayer);
+ ASSERT_TRUE(status) << "Failed to Encode: " << mFileName;
+
+ MP4HintTrack hintTrack;
+ status = PVGetHintTrack(mEncodeControl, &hintTrack);
+ ASSERT_TRUE(status) << "Failed to get hint track!";
+ UChar *overrunBuffer = PVGetOverrunBuffer(mEncodeControl);
+ ASSERT_EQ(overrunBuffer, nullptr) << "Overrun of buffer!";
+
+ int64_t numBytes = fwrite(mOutputBuffer, 1, dataLength, mFpOutput);
+ ASSERT_EQ(numBytes, dataLength) << "Failed to write to the output file!";
+ numEncodedFrames++;
+ numFramesToEncode--;
+ }
+}
+
+TEST_P(Mpeg4H263EncoderTest, EncodeTest) {
+ mInputBuffer = (uint8_t *)malloc((mFrameWidth * mFrameWidth * 3) / 2);
+ ASSERT_NE(mInputBuffer, nullptr) << "Failed to allocate the input buffer!";
+
+ mOutputBuffer = (uint8_t *)malloc(mOutputBufferSize);
+ ASSERT_NE(mOutputBuffer, nullptr) << "Failed to allocate the output buffer!";
+
+ mFpInput = fopen(mFileName.c_str(), "rb");
+ ASSERT_NE(mFpInput, nullptr) << "Failed to open the input file: " << mFileName;
+
+ mFpOutput = fopen(ENCODED_FILE, "wb");
+ ASSERT_NE(mFpOutput, nullptr) << "Failed to open the output file:" << ENCODED_FILE;
+
+ bool status = PVInitVideoEncoder(mEncodeControl, mEncodeHandle);
+ ASSERT_TRUE(status) << "Failed to initialize the encoder!";
+
+ // Get VOL header.
+ int32_t size = mOutputBufferSize;
+ status = PVGetVolHeader(mEncodeControl, mOutputBuffer, &size, 0);
+ ASSERT_TRUE(status) << "Failed to get the VOL header!";
+
+ // Write the VOL header on the first frame.
+ int32_t numBytes = fwrite(mOutputBuffer, 1, size, mFpOutput);
+ ASSERT_EQ(numBytes, size) << "Failed to write the VOL header!";
+
+ int64_t totalFrames = getTotalFrames();
+ ASSERT_NO_FATAL_FAILURE(processEncoder(totalFrames)) << "Failed to Encode: " << mFileName;
+ status = PVCleanUpVideoEncoder(mEncodeControl);
+ ASSERT_TRUE(status) << "Failed to clean up the encoder resources!";
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ EncodeTest, Mpeg4H263EncoderTest,
+ ::testing::Values(
+ make_tuple("bbb_352x288_420p_30fps_32frames.yuv", false, 352, 288, 25, 1024),
+ make_tuple("bbb_352x288_420p_30fps_32frames.yuv", true, 352, 288, 25, 1024),
+ make_tuple("bbb_352x288_420p_30fps_32frames.yuv", false, 176, 144, 25, 1024),
+ make_tuple("bbb_352x288_420p_30fps_32frames.yuv", true, 176, 144, 25, 1024),
+ make_tuple("football_qvga.yuv", false, 352, 288, 25, 1024),
+ make_tuple("football_qvga.yuv", true, 352, 288, 25, 1024),
+ make_tuple("football_qvga.yuv", false, 176, 144, 30, 1024),
+ make_tuple("football_qvga.yuv", true, 176, 144, 30, 1024)));
+
+int32_t main(int argc, char **argv) {
+ gEnv = new Mpeg4H263EncoderTestEnvironment();
+ ::testing::AddGlobalTestEnvironment(gEnv);
+ ::testing::InitGoogleTest(&argc, argv);
+ uint8_t status = gEnv->initFromOptions(argc, argv);
+ if (status == 0) {
+ status = RUN_ALL_TESTS();
+ ALOGI("Encoder Test Result = %d\n", status);
+ }
+ return status;
+}
diff --git a/media/libstagefright/codecs/m4v_h263/enc/test/Mpeg4H263EncoderTestEnvironment.h b/media/libstagefright/codecs/m4v_h263/enc/test/Mpeg4H263EncoderTestEnvironment.h
new file mode 100644
index 0000000..7ee36e0
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/enc/test/Mpeg4H263EncoderTestEnvironment.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MPEG4_H263_ENCODER_TEST_ENVIRONMENT_H__
+#define __MPEG4_H263_ENCODER_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class Mpeg4H263EncoderTestEnvironment : public::testing::Environment {
+ public:
+ Mpeg4H263EncoderTestEnvironment() : res("/data/local/tmp/Mpeg4H263EncoderTest/") {}
+
+ // Parses the command line arguments
+ int initFromOptions(int argc, char **argv);
+
+ void setRes(const char *_res) { res = _res; }
+
+ const string getRes() const { return res; }
+
+ private:
+ string res;
+};
+
+int Mpeg4H263EncoderTestEnvironment::initFromOptions(int argc, char **argv) {
+ static struct option options[] = {{"path", required_argument, 0, 'P'}, {0, 0, 0, 0}};
+
+ while (true) {
+ int index = 0;
+ int c = getopt_long(argc, argv, "P:", options, &index);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'P': {
+ setRes(optarg);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr,
+ "unrecognized option: %s\n\n"
+ "usage: %s <gtest options> <test options>\n\n"
+ "test options are:\n\n"
+ "-P, --path: Resource files directory location\n",
+ argv[optind ?: 1], argv[0]);
+ return 2;
+ }
+ return 0;
+}
+
+#endif // __MPEG4_H263_ENCODER_TEST_ENVIRONMENT_H__
diff --git a/media/libstagefright/codecs/m4v_h263/enc/test/README.md b/media/libstagefright/codecs/m4v_h263/enc/test/README.md
new file mode 100644
index 0000000..25de878
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/enc/test/README.md
@@ -0,0 +1,38 @@
+## Media Testing ##
+---
+
+#### Mpeg4H263Encoder :
+The Mpeg4H263Encoder Test Suite validates the Mpeg4 and H263 encoder available in libstagefright.
+Run the following steps to build the test suite:
+```
+m Mpeg4H263EncoderTest
+```
+
+The 32-bit binaries will be created in the following path : ${OUT}/data/nativetest/
+The 64-bit binaries will be created in the following path : ${OUT}/data/nativetest64/
+
+To test 64-bit binary push binaries from nativetest64.
+```
+adb push ${OUT}/data/nativetest64/Mpeg4H263EncoderTest/Mpeg4H263EncoderTest /data/local/tmp/
+```
+
+To test 32-bit binary push binaries from nativetest.
+```
+adb push ${OUT}/data/nativetest/Mpeg4H263EncoderTest/Mpeg4H263EncoderTest /data/local/tmp/
+```
+
+The resource file for the tests is taken from [here](https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/codecs/m4v_h263/enc/test/Mpeg4H263Encoder.zip ) Download, unzip and push these files into device for testing.
+
+```
+adb push Mpeg4H263Encoder/. /data/local/tmp/
+```
+
+usage: Mpeg4H263EncoderTest -P \<path_to_folder\>
+```
+adb shell /data/local/tmp/Mpeg4H263EncoderTest -P /data/local/tmp/
+```
+Alternatively, the test can also be run using atest command.
+
+```
+atest Mpeg4H263EncoderTest -- --enable-module-dynamic-download=true
+```
diff --git a/media/libstagefright/codecs/mp3dec/test/AndroidTest.xml b/media/libstagefright/codecs/mp3dec/test/AndroidTest.xml
new file mode 100644
index 0000000..7ff9732
--- /dev/null
+++ b/media/libstagefright/codecs/mp3dec/test/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Test module config for Mp3 Decoder unit test">
+ <option name="test-suite-tag" value="Mp3DecoderTest" />
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="Mp3DecoderTest->/data/local/tmp/Mp3DecoderTest" />
+ <option name="push-file"
+ key="https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/mp3dec/test/Mp3DecoderTest.zip?unzip=true"
+ value="/data/local/tmp/Mp3DecoderTestRes/" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="Mp3DecoderTest" />
+ <option name="native-test-flag" value="-P /data/local/tmp/Mp3DecoderTestRes/" />
+ </test>
+</configuration>
diff --git a/media/libstagefright/codecs/mp3dec/test/README.md b/media/libstagefright/codecs/mp3dec/test/README.md
index 019239e..f59fec7 100644
--- a/media/libstagefright/codecs/mp3dec/test/README.md
+++ b/media/libstagefright/codecs/mp3dec/test/README.md
@@ -22,13 +22,18 @@
adb push ${OUT}/data/nativetest/Mp3DecoderTest/Mp3DecoderTest /data/local/tmp/
```
-The resource file for the tests is taken from [here](https://drive.google.com/drive/folders/13cM4tAaVFrmr-zGFqaAzFBbKs75pnm9b). Push these files into device for testing.
-Download mp3 folder and push all the files in this folder to /data/local/tmp/ on the device.
+The resource file for the tests is taken from [here](https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/mp3dec/test/Mp3DecoderTest.zip). Download, unzip and push these files into device for testing.
+
```
-adb push mp3/. /data/local/tmp/
+adb push Mp3DecoderTestRes/. /data/local/tmp/
```
usage: Mp3DecoderTest -P \<path_to_folder\>
```
-adb shell /data/local/tmp/Mp3DecoderTest -P /data/local/tmp/
+adb shell /data/local/tmp/Mp3DecoderTest -P /data/local/tmp/Mp3DecoderTestRes/
+```
+Alternatively, the test can also be run using atest command.
+
+```
+atest Mp3DecoderTest -- --enable-module-dynamic-download=true
```
diff --git a/media/libstagefright/flac/dec/test/Android.bp b/media/libstagefright/flac/dec/test/Android.bp
new file mode 100644
index 0000000..70ca80a
--- /dev/null
+++ b/media/libstagefright/flac/dec/test/Android.bp
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+cc_test {
+ name: "FlacDecoderTest",
+ gtest: true,
+
+ srcs: [
+ "FlacDecoderTest.cpp",
+ ],
+
+ shared_libs: [
+ "liblog",
+ ],
+
+ static_libs: [
+ "libstagefright_flacdec",
+ "libFLAC",
+ ],
+
+ header_libs: [
+ "libstagefright_foundation_headers",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ sanitize: {
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ cfi: true,
+ },
+}
diff --git a/media/libstagefright/flac/dec/test/AndroidTest.xml b/media/libstagefright/flac/dec/test/AndroidTest.xml
new file mode 100644
index 0000000..bebba8e
--- /dev/null
+++ b/media/libstagefright/flac/dec/test/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Test module config for flac decoder unit tests">
+ <option name="test-suite-tag" value="FlacDecoderTest" />
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="FlacDecoderTest->/data/local/tmp/FlacDecoderTest/" />
+ <option name="push-file"
+ key="https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/flac/dec/test/FlacDecoder.zip?unzip=true"
+ value="/data/local/tmp/FlacDecoderTestRes/" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="FlacDecoderTest" />
+ <option name="native-test-flag" value="-P /data/local/tmp/FlacDecoderTestRes/" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/media/libstagefright/flac/dec/test/FlacDecoderTest.cpp b/media/libstagefright/flac/dec/test/FlacDecoderTest.cpp
new file mode 100644
index 0000000..34f12db
--- /dev/null
+++ b/media/libstagefright/flac/dec/test/FlacDecoderTest.cpp
@@ -0,0 +1,270 @@
+/*
+ * 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 "FlacDecoderTest"
+
+#include <utils/Log.h>
+#include <fstream>
+
+#include "FLACDecoder.h"
+
+#include "FlacDecoderTestEnvironment.h"
+
+#define OUTPUT_FILE_NAME "/data/local/tmp/FlacDecoderOutput.raw"
+#define CODEC_CONFIG_FLAG 32
+
+constexpr uint32_t kMaxCount = 10;
+constexpr int32_t kMaxBlockSize = 4096;
+
+using namespace android;
+
+struct FrameInfo {
+ int32_t bytesCount;
+ uint32_t flags;
+ int64_t timestamp;
+};
+
+static FlacDecoderTestEnvironment *gEnv = nullptr;
+
+class FLACDecoderTest : public ::testing::TestWithParam<tuple<string, string, bool>> {
+ public:
+ FLACDecoderTest() : mFLACDecoder(nullptr), mHasStreamInfo(false), mInputBufferCount(0) {}
+
+ ~FLACDecoderTest() {
+ if (mEleStream.is_open()) mEleStream.close();
+ if (mFLACDecoder) delete mFLACDecoder;
+ mFLACDecoder = nullptr;
+ }
+
+ virtual void SetUp() override {
+ mFLACDecoder = FLACDecoder::Create();
+ ASSERT_NE(mFLACDecoder, nullptr) << "initDecoder: failed to create FLACDecoder";
+ }
+
+ int32_t processFlacDecoder(vector<FrameInfo> Info, int32_t offset, int32_t range,
+ bool outputFloat, ofstream &ostrm);
+
+ FLACDecoder *mFLACDecoder;
+ FLAC__StreamMetadata_StreamInfo mStreamInfo;
+
+ bool mHasStreamInfo;
+ int32_t mInputBufferCount;
+ ifstream mEleStream;
+};
+
+void getInfo(string infoFileName, vector<FrameInfo> &Info) {
+ ifstream eleInfo;
+ eleInfo.open(infoFileName);
+ ASSERT_EQ(eleInfo.is_open(), true);
+ int32_t bytesCount = 0;
+ uint32_t flags = 0;
+ uint32_t timestamp = 0;
+ while (1) {
+ if (!(eleInfo >> bytesCount)) break;
+ eleInfo >> flags;
+ eleInfo >> timestamp;
+ Info.push_back({bytesCount, flags, timestamp});
+ }
+ if (eleInfo.is_open()) eleInfo.close();
+}
+
+int32_t FLACDecoderTest::processFlacDecoder(vector<FrameInfo> Info, int32_t offset, int32_t range,
+ bool outputFloat, ofstream &ostrm) {
+ memset(&mStreamInfo, 0, sizeof(mStreamInfo));
+
+ int32_t frameID = offset;
+ if (range + offset > Info.size() || range < 0 || offset > Info.size() - 1 || offset < 0) {
+ ALOGE("Invalid offset or range or both passed for decoding");
+ ALOGE("offset = %d \t range = %d \t Info.size() = %zu", offset, range, Info.size());
+ return -1;
+ }
+
+ while (1) {
+ if (frameID == Info.size() || frameID == (offset + range)) break;
+ int64_t flags = (Info)[frameID].flags;
+ int32_t size = (Info)[frameID].bytesCount;
+ if (size < 0) {
+ ALOGE("Size for the memory allocation is negative");
+ return -1;
+ }
+ char *data = (char *)malloc(size);
+ if (!data) {
+ ALOGE("Insufficient memory to read frame");
+ return -1;
+ }
+
+ mEleStream.read(data, size);
+ if (mEleStream.gcount() != size) {
+ if (data) {
+ free(data);
+ data = nullptr;
+ }
+ ALOGE("Invalid size read, requested: %d and read: %zu", size, mEleStream.gcount());
+ return -1;
+ }
+
+ if (flags == CODEC_CONFIG_FLAG && mInputBufferCount == 0) {
+ status_t decoderErr = mFLACDecoder->parseMetadata((uint8_t *)data, size);
+ if (decoderErr == WOULD_BLOCK) {
+ ALOGV("process: parseMetadata is Blocking, Continue %d", decoderErr);
+ } else if (decoderErr == OK) {
+ mStreamInfo = mFLACDecoder->getStreamInfo();
+ if (mStreamInfo.sample_rate && mStreamInfo.max_blocksize && mStreamInfo.channels) {
+ mHasStreamInfo = true;
+ }
+ ALOGV("decoder configuration : %d Hz, %d channels, %d samples,"
+ " %d block size",
+ mStreamInfo.sample_rate, mStreamInfo.channels,
+ (int32_t)mStreamInfo.total_samples, mStreamInfo.max_blocksize);
+ } else {
+ ALOGE("FLACDecoder parseMetaData returns error %d", decoderErr);
+ if (data) {
+ free(data);
+ data = nullptr;
+ }
+ return decoderErr;
+ }
+ } else {
+ const size_t sampleSize = outputFloat ? sizeof(float) : sizeof(int16_t);
+ size_t outSize = mHasStreamInfo
+ ? mStreamInfo.max_blocksize * mStreamInfo.channels * sampleSize
+ : kMaxBlockSize * FLACDecoder::kMaxChannels * sampleSize;
+
+ void *out_buf = malloc(outSize);
+ if (!out_buf) {
+ if (data) {
+ free(data);
+ data = nullptr;
+ }
+ ALOGE("Output buffer allocation failed");
+ return -1;
+ }
+ status_t decoderErr = mFLACDecoder->decodeOneFrame((uint8_t *)data, size, out_buf,
+ &outSize, outputFloat);
+ if (decoderErr != OK) {
+ ALOGE("decodeOneFrame returns error %d", decoderErr);
+ if (data) {
+ free(data);
+ data = nullptr;
+ }
+ if (out_buf) {
+ free(out_buf);
+ out_buf = nullptr;
+ }
+ return decoderErr;
+ }
+ ostrm.write(reinterpret_cast<char *>(out_buf), outSize);
+ free(out_buf);
+ out_buf = nullptr;
+ }
+ mInputBufferCount++;
+ frameID++;
+ free(data);
+ data = nullptr;
+ }
+ ALOGV("frameID=%d", frameID);
+ return 0;
+}
+
+TEST_F(FLACDecoderTest, CreateDeleteTest) {
+ if (mFLACDecoder) delete mFLACDecoder;
+ mFLACDecoder = nullptr;
+
+ for (int32_t i = 0; i < kMaxCount; i++) {
+ mFLACDecoder = FLACDecoder::Create();
+ ASSERT_NE(mFLACDecoder, nullptr) << "FLACDecoder Creation Failed";
+ if (mFLACDecoder) delete mFLACDecoder;
+ mFLACDecoder = nullptr;
+ }
+}
+
+TEST_P(FLACDecoderTest, FlushTest) {
+ tuple<string /* InputFileName */, string /* InfoFileName */, bool /* outputfloat */> params =
+ GetParam();
+
+ string inputFileName = gEnv->getRes() + get<0>(params);
+ string infoFileName = gEnv->getRes() + get<1>(params);
+ bool outputFloat = get<2>(params);
+
+ vector<FrameInfo> Info;
+ getInfo(infoFileName, Info);
+
+ mEleStream.open(inputFileName, ifstream::binary);
+ ASSERT_EQ(mEleStream.is_open(), true);
+
+ ofstream ostrm;
+ ostrm.open(OUTPUT_FILE_NAME, std::ofstream::binary);
+ ASSERT_EQ(ostrm.is_open(), true);
+
+ int32_t status = processFlacDecoder(Info, 0, Info.size() / 3, outputFloat, ostrm);
+ ASSERT_EQ(status, 0) << "Test Failed. Decode returned error = " << status << endl;
+ mFLACDecoder->flush();
+ mHasStreamInfo = false;
+ status = processFlacDecoder(Info, (Info.size() / 3), Info.size() - (Info.size() / 3),
+ outputFloat, ostrm);
+ ostrm.close();
+ Info.clear();
+ ASSERT_EQ(status, 0) << "Test Failed. Decode returned error = " << status << endl;
+}
+
+TEST_P(FLACDecoderTest, DecodeTest) {
+ tuple<string /* InputFileName */, string /* InfoFileName */, bool /* outputfloat */> params =
+ GetParam();
+
+ string inputFileName = gEnv->getRes() + get<0>(params);
+ string infoFileName = gEnv->getRes() + get<1>(params);
+ bool outputFloat = get<2>(params);
+
+ vector<FrameInfo> Info;
+ getInfo(infoFileName, Info);
+
+ mEleStream.open(inputFileName, ifstream::binary);
+ ASSERT_EQ(mEleStream.is_open(), true);
+
+ ofstream ostrm;
+ ostrm.open(OUTPUT_FILE_NAME, std::ofstream::binary);
+ ASSERT_EQ(ostrm.is_open(), true);
+
+ int32_t status = processFlacDecoder(Info, 0, Info.size(), outputFloat, ostrm);
+ ostrm.close();
+ Info.clear();
+ ASSERT_EQ(status, 0) << "Test Failed. Decode returned error = " << status << endl;
+}
+
+// TODO: Add remaining tests
+INSTANTIATE_TEST_SUITE_P(
+ FLACDecoderTestAll, FLACDecoderTest,
+ ::testing::Values(make_tuple("bbb_flac_stereo_680kbps_48000hz.flac",
+ "bbb_flac_stereo_680kbps_48000hz.info", true),
+ make_tuple("bbb_flac_stereo_680kbps_48000hz.flac",
+ "bbb_flac_stereo_680kbps_48000hz.info", false),
+ make_tuple("bbb_flac_stereo_600kbps_44100hz.flac",
+ "bbb_flac_stereo_600kbps_44100hz.info", true),
+ make_tuple("bbb_flac_stereo_600kbps_44100hz.flac",
+ "bbb_flac_stereo_600kbps_44100hz.info", false)));
+
+int main(int argc, char **argv) {
+ gEnv = new FlacDecoderTestEnvironment();
+ ::testing::AddGlobalTestEnvironment(gEnv);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = gEnv->initFromOptions(argc, argv);
+ if (status == 0) {
+ status = RUN_ALL_TESTS();
+ ALOGV("Flac Decoder Test Result = %d\n", status);
+ }
+ return status;
+}
diff --git a/media/libstagefright/flac/dec/test/FlacDecoderTestEnvironment.h b/media/libstagefright/flac/dec/test/FlacDecoderTestEnvironment.h
new file mode 100644
index 0000000..1334bba
--- /dev/null
+++ b/media/libstagefright/flac/dec/test/FlacDecoderTestEnvironment.h
@@ -0,0 +1,73 @@
+/*
+ * 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 __FLAC_DECODER_TEST_ENVIRONMENT_H__
+#define __FLAC_DECODER_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class FlacDecoderTestEnvironment : public ::testing::Environment {
+ public:
+ FlacDecoderTestEnvironment() : res("/data/local/tmp/") {}
+
+ // Parses the command line arguments
+ int initFromOptions(int argc, char **argv);
+
+ void setRes(const char *_res) { res = _res; }
+
+ const string getRes() const { return res; }
+
+ private:
+ string res;
+};
+
+int FlacDecoderTestEnvironment::initFromOptions(int argc, char **argv) {
+ static struct option options[] = {{"path", required_argument, 0, 'P'}, {0, 0, 0, 0}};
+
+ while (true) {
+ int index = 0;
+ int c = getopt_long(argc, argv, "P:", options, &index);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'P': {
+ setRes(optarg);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr,
+ "unrecognized option: %s\n\n"
+ "usage: %s <gtest options> <test options>\n\n"
+ "test options are:\n\n"
+ "-P, --path: Resource files directory location\n",
+ argv[optind ?: 1], argv[0]);
+ return 2;
+ }
+ return 0;
+}
+
+#endif // __FLAC_DECODER_TEST_ENVIRONMENT_H__
diff --git a/media/libstagefright/flac/dec/test/README.md b/media/libstagefright/flac/dec/test/README.md
new file mode 100644
index 0000000..4d194cd
--- /dev/null
+++ b/media/libstagefright/flac/dec/test/README.md
@@ -0,0 +1,40 @@
+## Media Testing ##
+---
+#### FlacDecoder :
+The FlacDecoder Test Suite validates the FlacDecoder available in libstagefright.
+
+Run the following steps to build the test suite:
+```
+m FlacDecoderTest
+```
+
+The 32-bit binaries will be created in the following path : ${OUT}/data/nativetest/
+
+The 64-bit binaries will be created in the following path : ${OUT}/data/nativetest64/
+
+To test 64-bit binary push binaries from nativetest64.
+```
+adb push ${OUT}/data/nativetest64/FlacDecoderTest/FlacDecoderTest /data/local/tmp/
+```
+
+To test 32-bit binary push binaries from nativetest.
+```
+adb push ${OUT}/data/nativetest/FlacDecoderTest/FlacDecoderTest /data/local/tmp/
+```
+
+The resource file for the tests is taken from [here](https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/flac/dec/test/FlacDecoder.zip).
+Download, unzip and push these files into device for testing.
+
+```
+adb push FlacDecoder /data/local/tmp/
+```
+
+usage: FlacDecoderTest -P \<path_to_folder\>
+```
+adb shell /data/local/tmp/FlacDecoderTest -P /data/local/tmp/FlacDecoder/
+```
+Alternatively, the test can also be run using atest command.
+
+```
+atest FlacDecoderTest -- --enable-module-dynamic-download=true
+```
diff --git a/media/ndk/NdkJavaVMHelper.cpp b/media/ndk/NdkJavaVMHelper.cpp
index baf2744..a77016c 100644
--- a/media/ndk/NdkJavaVMHelper.cpp
+++ b/media/ndk/NdkJavaVMHelper.cpp
@@ -16,7 +16,7 @@
#define LOG_TAG "NdkJavaVMHelper"
-#include <media/NdkJavaVMHelper.h>
+#include "NdkJavaVMHelperPriv.h"
#include <utils/Log.h>
namespace android {
diff --git a/media/ndk/include/media/NdkJavaVMHelper.h b/media/ndk/NdkJavaVMHelperPriv.h
similarity index 86%
rename from media/ndk/include/media/NdkJavaVMHelper.h
rename to media/ndk/NdkJavaVMHelperPriv.h
index 1c20275..49f087e 100644
--- a/media/ndk/include/media/NdkJavaVMHelper.h
+++ b/media/ndk/NdkJavaVMHelperPriv.h
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-#ifndef NDK_JAVA_VM_HELPER_H_
+#ifndef NDK_JAVA_VM_HELPER_PRIV_H_
-#define NDK_JAVA_VM_HELPER_H_
+#define NDK_JAVA_VM_HELPER_PRIV_H_
#include "jni.h"
@@ -28,4 +28,4 @@
} // namespace android
-#endif // NDK_JAVA_VM_HELPER_H_
\ No newline at end of file
+#endif // NDK_JAVA_VM_HELPER_PRIV_H_
\ No newline at end of file
diff --git a/media/ndk/NdkMediaDataSource.cpp b/media/ndk/NdkMediaDataSource.cpp
index 98ccd6c..3e6d7d6 100644
--- a/media/ndk/NdkMediaDataSource.cpp
+++ b/media/ndk/NdkMediaDataSource.cpp
@@ -17,6 +17,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "NdkMediaDataSource"
+#include "NdkJavaVMHelperPriv.h"
#include "NdkMediaDataSourcePriv.h"
#include <inttypes.h>
@@ -30,7 +31,6 @@
#include <datasource/HTTPBase.h>
#include <datasource/NuCachedSource2.h>
#include <media/IMediaHTTPService.h>
-#include <media/NdkJavaVMHelper.h>
#include <media/NdkMediaError.h>
#include <media/NdkMediaDataSource.h>
#include <media/stagefright/InterfaceUtils.h>
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/Android.bp b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/Android.bp
index c72eb55..3e5e4c8 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/Android.bp
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/Android.bp
@@ -1,5 +1,6 @@
cc_test_library {
name: "libmediabenchmark_jni",
+ sdk_version: "current",
defaults: [
"libmediabenchmark_common-defaults",
diff --git a/media/tests/benchmark/src/native/common/Android.bp b/media/tests/benchmark/src/native/common/Android.bp
index f8ea25c..d4389da 100644
--- a/media/tests/benchmark/src/native/common/Android.bp
+++ b/media/tests/benchmark/src/native/common/Android.bp
@@ -46,6 +46,8 @@
cc_defaults {
name: "libmediabenchmark-defaults",
+ sdk_version: "current",
+ stl: "c++_shared",
shared_libs: [
"libmediandk",
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index b20774a..2c97a38 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -1352,29 +1352,9 @@
// Get list of concurrent streaming camera device combinations
if (mMinorVersion >= 6) {
- hardware::Return<void> ret = interface2_6->getConcurrentStreamingCameraIds([&status, this](
- Status concurrentIdStatus, // TODO: Move all instances of hidl_string to 'using'
- const hardware::hidl_vec<hardware::hidl_vec<hardware::hidl_string>>&
- cameraDeviceIdCombinations) {
- status = concurrentIdStatus;
- if (status == Status::OK) {
- for (auto& combination : cameraDeviceIdCombinations) {
- std::unordered_set<std::string> deviceIds;
- for (auto &cameraDeviceId : combination) {
- deviceIds.insert(cameraDeviceId.c_str());
- }
- mConcurrentCameraIdCombinations.push_back(std::move(deviceIds));
- }
- } });
- if (!ret.isOk()) {
- ALOGE("%s: Transaction error in getting camera ID list from provider '%s': %s",
- __FUNCTION__, mProviderName.c_str(), linked.description().c_str());
- return DEAD_OBJECT;
- }
- if (status != Status::OK) {
- ALOGE("%s: Unable to query for camera devices from provider '%s'",
- __FUNCTION__, mProviderName.c_str());
- return mapToStatusT(status);
+ res = getConcurrentStreamingCameraIdsInternalLocked(interface2_6);
+ if (res != OK) {
+ return res;
}
}
@@ -1626,6 +1606,75 @@
return OK;
}
+status_t CameraProviderManager::ProviderInfo::getConcurrentStreamingCameraIdsInternalLocked(
+ sp<provider::V2_6::ICameraProvider> &interface2_6) {
+ if (interface2_6 == nullptr) {
+ ALOGE("%s: null interface provided", __FUNCTION__);
+ return BAD_VALUE;
+ }
+ Status status = Status::OK;
+ hardware::Return<void> ret =
+ interface2_6->getConcurrentStreamingCameraIds([&status, this](
+ Status concurrentIdStatus, // TODO: Move all instances of hidl_string to 'using'
+ const hardware::hidl_vec<hardware::hidl_vec<hardware::hidl_string>>&
+ cameraDeviceIdCombinations) {
+ status = concurrentIdStatus;
+ if (status == Status::OK) {
+ mConcurrentCameraIdCombinations.clear();
+ for (auto& combination : cameraDeviceIdCombinations) {
+ std::unordered_set<std::string> deviceIds;
+ for (auto &cameraDeviceId : combination) {
+ deviceIds.insert(cameraDeviceId.c_str());
+ }
+ mConcurrentCameraIdCombinations.push_back(std::move(deviceIds));
+ }
+ } });
+ if (!ret.isOk()) {
+ ALOGE("%s: Transaction error in getting concurrent camera ID list from provider '%s'",
+ __FUNCTION__, mProviderName.c_str());
+ return DEAD_OBJECT;
+ }
+ if (status != Status::OK) {
+ ALOGE("%s: Unable to query for camera devices from provider '%s'",
+ __FUNCTION__, mProviderName.c_str());
+ return mapToStatusT(status);
+ }
+ return OK;
+}
+
+status_t CameraProviderManager::ProviderInfo::reCacheConcurrentStreamingCameraIdsLocked() {
+ if (mMinorVersion < 6) {
+ // Unsupported operation, nothing to do here
+ return OK;
+ }
+ // 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) {
+ ALOGE("%s: camera provider interface for %s is not valid", __FUNCTION__,
+ mProviderName.c_str());
+ return INVALID_OPERATION;
+ }
+ auto castResult = provider::V2_6::ICameraProvider::castFrom(interface);
+
+ if (castResult.isOk()) {
+ sp<provider::V2_6::ICameraProvider> interface2_6 = castResult;
+ if (interface2_6 != nullptr) {
+ return getConcurrentStreamingCameraIdsInternalLocked(interface2_6);
+ } else {
+ // This should not happen since mMinorVersion >= 6
+ ALOGE("%s: mMinorVersion was >= 6, but interface2_6 was nullptr", __FUNCTION__);
+ return UNKNOWN_ERROR;
+ }
+ }
+ return OK;
+}
+
+std::vector<std::unordered_set<std::string>>
+CameraProviderManager::ProviderInfo::getConcurrentCameraIdCombinations() {
+ std::lock_guard<std::mutex> lock(mLock);
+ return mConcurrentCameraIdCombinations;
+}
+
hardware::Return<void> CameraProviderManager::ProviderInfo::cameraDeviceStatusChange(
const hardware::hidl_string& cameraDeviceName,
CameraDeviceStatus newStatus) {
@@ -1659,6 +1708,10 @@
}
listener = mManager->getStatusListener();
initialized = mInitialized;
+ if (reCacheConcurrentStreamingCameraIdsLocked() != OK) {
+ ALOGE("%s: CameraProvider %s could not re-cache concurrent streaming camera id list ",
+ __FUNCTION__, mProviderName.c_str());
+ }
}
// Call without lock held to allow reentrancy into provider manager
// Don't send the callback if providerInfo hasn't been initialized.
@@ -2747,7 +2800,7 @@
std::vector<std::unordered_set<std::string>> deviceIdCombinations;
std::lock_guard<std::mutex> lock(mInterfaceMutex);
for (auto &provider : mProviders) {
- for (auto &combinations : provider->mConcurrentCameraIdCombinations) {
+ for (auto &combinations : provider->getConcurrentCameraIdCombinations()) {
deviceIdCombinations.push_back(combinations);
}
}
@@ -2831,7 +2884,7 @@
// TODO: we should also do a findDeviceInfoLocked here ?
for (auto &provider : mProviders) {
if (checkIfSetContainsAll(cameraIdsAndSessionConfigs,
- provider->mConcurrentCameraIdCombinations)) {
+ provider->getConcurrentCameraIdCombinations())) {
// For each camera device in cameraIdsAndSessionConfigs collect
// the streamConfigs and create the HAL
// CameraIdAndStreamCombination, exit early if needed
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index 42da227..1a02980 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -417,6 +417,9 @@
status_t notifyDeviceStateChange(
hardware::hidl_bitfield<hardware::camera::provider::V2_5::DeviceState>
newDeviceState);
+
+ std::vector<std::unordered_set<std::string>> getConcurrentCameraIdCombinations();
+
/**
* Query the camera provider for concurrent stream configuration support
*/
@@ -510,8 +513,6 @@
// physical camera IDs.
std::vector<std::string> mProviderPublicCameraIds;
- std::vector<std::unordered_set<std::string>> mConcurrentCameraIdCombinations;
-
// HALv1-specific camera fields, including the actual device interface
struct DeviceInfo1 : public DeviceInfo {
typedef hardware::camera::device::V1_0::ICameraDevice InterfaceT;
@@ -600,6 +601,8 @@
bool mInitialized = false;
+ std::vector<std::unordered_set<std::string>> mConcurrentCameraIdCombinations;
+
// Templated method to instantiate the right kind of DeviceInfo and call the
// right CameraProvider getCameraDeviceInterface_* method.
template<class DeviceInfoT>
@@ -623,6 +626,12 @@
static metadata_vendor_id_t generateVendorTagId(const std::string &name);
void removeDevice(std::string id);
+
+ // Expects to have mLock locked
+ status_t reCacheConcurrentStreamingCameraIdsLocked();
+ // Expects to have mLock locked
+ status_t getConcurrentStreamingCameraIdsInternalLocked(
+ sp<hardware::camera::provider::V2_6::ICameraProvider> &interface2_6);
};
// Utility to find a DeviceInfo by ID; pointer is only valid while mInterfaceMutex is held