Merge "Camera: Add HEIC support"
diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp
index 7803ccc..c361690 100644
--- a/cmds/screenrecord/screenrecord.cpp
+++ b/cmds/screenrecord/screenrecord.cpp
@@ -86,6 +86,7 @@
using android::INFO_FORMAT_CHANGED;
using android::INFO_OUTPUT_BUFFERS_CHANGED;
using android::INVALID_OPERATION;
+using android::NAME_NOT_FOUND;
using android::NO_ERROR;
using android::UNKNOWN_ERROR;
@@ -585,8 +586,12 @@
self->startThreadPool();
// Get main display parameters.
- sp<IBinder> mainDpy = SurfaceComposerClient::getBuiltInDisplay(
- ISurfaceComposer::eDisplayIdMain);
+ const sp<IBinder> mainDpy = SurfaceComposerClient::getInternalDisplayToken();
+ if (mainDpy == nullptr) {
+ fprintf(stderr, "ERROR: no display\n");
+ return NAME_NOT_FOUND;
+ }
+
DisplayInfo mainDpyInfo;
err = SurfaceComposerClient::getDisplayInfo(mainDpy, &mainDpyInfo);
if (err != NO_ERROR) {
diff --git a/cmds/stagefright/codec.cpp b/cmds/stagefright/codec.cpp
index a463ec5..e5a4337 100644
--- a/cmds/stagefright/codec.cpp
+++ b/cmds/stagefright/codec.cpp
@@ -411,10 +411,12 @@
composerClient = new SurfaceComposerClient;
CHECK_EQ(composerClient->initCheck(), (status_t)OK);
- sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(
- ISurfaceComposer::eDisplayIdMain));
+ const sp<IBinder> display = SurfaceComposerClient::getInternalDisplayToken();
+ CHECK(display != nullptr);
+
DisplayInfo info;
- SurfaceComposerClient::getDisplayInfo(display, &info);
+ CHECK_EQ(SurfaceComposerClient::getDisplayInfo(display, &info), NO_ERROR);
+
ssize_t displayWidth = info.w;
ssize_t displayHeight = info.h;
diff --git a/cmds/stagefright/mediafilter.cpp b/cmds/stagefright/mediafilter.cpp
index f0ee0e1..2cf6955 100644
--- a/cmds/stagefright/mediafilter.cpp
+++ b/cmds/stagefright/mediafilter.cpp
@@ -748,10 +748,12 @@
composerClient = new SurfaceComposerClient;
CHECK_EQ((status_t)OK, composerClient->initCheck());
- android::sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(
- ISurfaceComposer::eDisplayIdMain));
+ const android::sp<IBinder> display = SurfaceComposerClient::getInternalDisplayToken();
+ CHECK(display != nullptr);
+
DisplayInfo info;
- SurfaceComposerClient::getDisplayInfo(display, &info);
+ CHECK_EQ(SurfaceComposerClient::getDisplayInfo(display, &info), NO_ERROR);
+
ssize_t displayWidth = info.w;
ssize_t displayHeight = info.h;
diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp
index b2f39dc..35bdbc0 100644
--- a/cmds/stagefright/stream.cpp
+++ b/cmds/stagefright/stream.cpp
@@ -318,10 +318,12 @@
sp<SurfaceComposerClient> composerClient = new SurfaceComposerClient;
CHECK_EQ(composerClient->initCheck(), (status_t)OK);
- sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(
- ISurfaceComposer::eDisplayIdMain));
+ const sp<IBinder> display = SurfaceComposerClient::getInternalDisplayToken();
+ CHECK(display != nullptr);
+
DisplayInfo info;
- SurfaceComposerClient::getDisplayInfo(display, &info);
+ CHECK_EQ(SurfaceComposerClient::getDisplayInfo(display, &info), NO_ERROR);
+
ssize_t displayWidth = info.w;
ssize_t displayHeight = info.h;
diff --git a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp
index 27bd631..bf35224 100644
--- a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp
+++ b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp
@@ -97,13 +97,13 @@
///////////////////////////////////////////////////////////////////////////////
ClearKeyCasPlugin::ClearKeyCasPlugin(
void *appData, CasPluginCallback callback)
- : mCallback(callback), mAppData(appData) {
+ : mCallback(callback), mCallbackExt(NULL), mAppData(appData) {
ALOGV("CTOR");
}
ClearKeyCasPlugin::ClearKeyCasPlugin(
void *appData, CasPluginCallbackExt callback)
- : mCallbackExt(callback), mAppData(appData) {
+ : mCallback(NULL), mCallbackExt(callback), mAppData(appData) {
ALOGV("CTOR");
}
diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h
index c38e674..b9f3aa8 100644
--- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h
+++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h
@@ -120,11 +120,9 @@
IComponentStore::ComponentTraits* d,
const C2Component::Traits& s);
-// ComponentTraits -> C2Component::Traits, std::unique_ptr<std::vector<std::string>>
-// Note: The output d is only valid as long as aliasesBuffer remains alive.
+// ComponentTraits -> C2Component::Traits
bool objcpy(
C2Component::Traits* d,
- std::unique_ptr<std::vector<std::string>>* aliasesBuffer,
const IComponentStore::ComponentTraits& s);
// C2StructDescriptor -> StructDescriptor
diff --git a/media/codec2/hidl/1.0/utils/types.cpp b/media/codec2/hidl/1.0/utils/types.cpp
index caed839..02cdc23 100644
--- a/media/codec2/hidl/1.0/utils/types.cpp
+++ b/media/codec2/hidl/1.0/utils/types.cpp
@@ -351,7 +351,6 @@
// ComponentTraits -> C2Component::Traits, std::unique_ptr<std::vector<std::string>>
bool objcpy(
C2Component::Traits* d,
- std::unique_ptr<std::vector<std::string>>* aliasesBuffer,
const IComponentStore::ComponentTraits& s) {
d->name = s.name.c_str();
@@ -394,15 +393,9 @@
d->rank = static_cast<C2Component::rank_t>(s.rank);
d->mediaType = s.mediaType.c_str();
-
- // aliasesBuffer must not be resized after this.
- *aliasesBuffer = std::make_unique<std::vector<std::string>>(
- s.aliases.size());
- (*aliasesBuffer)->resize(s.aliases.size());
- std::vector<C2StringLiteral> dAliases(s.aliases.size());
+ d->aliases.resize(s.aliases.size());
for (size_t i = 0; i < s.aliases.size(); ++i) {
- (**aliasesBuffer)[i] = s.aliases[i].c_str();
- d->aliases[i] = (**aliasesBuffer)[i].c_str();
+ d->aliases[i] = s.aliases[i];
}
return true;
}
diff --git a/media/codec2/hidl/client/client.cpp b/media/codec2/hidl/client/client.cpp
index e02c2ca..3808be5 100644
--- a/media/codec2/hidl/client/client.cpp
+++ b/media/codec2/hidl/client/client.cpp
@@ -564,9 +564,8 @@
return;
}
mTraitsList.resize(t.size());
- mAliasesBuffer.resize(t.size());
for (size_t i = 0; i < t.size(); ++i) {
- if (!objcpy(&mTraitsList[i], &mAliasesBuffer[i], t[i])) {
+ if (!objcpy(&mTraitsList[i], t[i])) {
LOG(ERROR) << "listComponents -- corrupted output.";
return;
}
diff --git a/media/codec2/hidl/client/include/codec2/hidl/client.h b/media/codec2/hidl/client/include/codec2/hidl/client.h
index 5b3afca..478ce6e 100644
--- a/media/codec2/hidl/client/include/codec2/hidl/client.h
+++ b/media/codec2/hidl/client/include/codec2/hidl/client.h
@@ -232,8 +232,6 @@
mutable bool mListed;
std::string mServiceName;
mutable std::vector<C2Component::Traits> mTraitsList;
- mutable std::vector<std::unique_ptr<std::vector<std::string>>>
- mAliasesBuffer;
sp<::android::hardware::media::bufferpool::V2_0::IClientManager>
mHostPoolManager;
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index ed1f85b..bc22045 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -937,6 +937,47 @@
(new AMessage(kWhatCreateInputSurface, this))->post();
}
+sp<PersistentSurface> CCodec::CreateOmxInputSurface() {
+ using namespace android::hardware::media::omx::V1_0;
+ using namespace android::hardware::media::omx::V1_0::utils;
+ using namespace android::hardware::graphics::bufferqueue::V1_0::utils;
+ typedef android::hardware::media::omx::V1_0::Status OmxStatus;
+ android::sp<IOmx> omx = IOmx::getService();
+ typedef android::hardware::graphics::bufferqueue::V1_0::
+ IGraphicBufferProducer HGraphicBufferProducer;
+ typedef android::hardware::media::omx::V1_0::
+ IGraphicBufferSource HGraphicBufferSource;
+ OmxStatus s;
+ android::sp<HGraphicBufferProducer> gbp;
+ android::sp<HGraphicBufferSource> gbs;
+ android::Return<void> transStatus = omx->createInputSurface(
+ [&s, &gbp, &gbs](
+ OmxStatus status,
+ const android::sp<HGraphicBufferProducer>& producer,
+ const android::sp<HGraphicBufferSource>& source) {
+ s = status;
+ gbp = producer;
+ gbs = source;
+ });
+ if (transStatus.isOk() && s == OmxStatus::OK) {
+ return new PersistentSurface(
+ new H2BGraphicBufferProducer(gbp),
+ sp<::android::IGraphicBufferSource>(new LWGraphicBufferSource(gbs)));
+ }
+
+ return nullptr;
+}
+
+sp<PersistentSurface> CCodec::CreateCompatibleInputSurface() {
+ sp<PersistentSurface> surface(CreateInputSurface());
+
+ if (surface == nullptr) {
+ surface = CreateOmxInputSurface();
+ }
+
+ return surface;
+}
+
void CCodec::createInputSurface() {
status_t err;
sp<IGraphicBufferProducer> bufferProducer;
@@ -949,7 +990,7 @@
outputFormat = config->mOutputFormat;
}
- std::shared_ptr<PersistentSurface> persistentSurface(CreateInputSurface());
+ sp<PersistentSurface> persistentSurface = CreateCompatibleInputSurface();
if (persistentSurface->getHidlTarget()) {
sp<IInputSurface> hidlInputSurface = IInputSurface::castFrom(
@@ -1669,46 +1710,17 @@
return new android::CCodec;
}
+// Create Codec 2.0 input surface
extern "C" android::PersistentSurface *CreateInputSurface() {
// Attempt to create a Codec2's input surface.
std::shared_ptr<android::Codec2Client::InputSurface> inputSurface =
android::Codec2Client::CreateInputSurface();
- if (inputSurface) {
- return new android::PersistentSurface(
- inputSurface->getGraphicBufferProducer(),
- static_cast<android::sp<android::hidl::base::V1_0::IBase>>(
- inputSurface->getHalInterface()));
+ if (!inputSurface) {
+ return nullptr;
}
-
- // Fall back to OMX.
- using namespace android::hardware::media::omx::V1_0;
- using namespace android::hardware::media::omx::V1_0::utils;
- using namespace android::hardware::graphics::bufferqueue::V1_0::utils;
- typedef android::hardware::media::omx::V1_0::Status OmxStatus;
- android::sp<IOmx> omx = IOmx::getService();
- typedef android::hardware::graphics::bufferqueue::V1_0::
- IGraphicBufferProducer HGraphicBufferProducer;
- typedef android::hardware::media::omx::V1_0::
- IGraphicBufferSource HGraphicBufferSource;
- OmxStatus s;
- android::sp<HGraphicBufferProducer> gbp;
- android::sp<HGraphicBufferSource> gbs;
- android::Return<void> transStatus = omx->createInputSurface(
- [&s, &gbp, &gbs](
- OmxStatus status,
- const android::sp<HGraphicBufferProducer>& producer,
- const android::sp<HGraphicBufferSource>& source) {
- s = status;
- gbp = producer;
- gbs = source;
- });
- if (transStatus.isOk() && s == OmxStatus::OK) {
- return new android::PersistentSurface(
- new H2BGraphicBufferProducer(gbp),
- sp<::android::IGraphicBufferSource>(
- new LWGraphicBufferSource(gbs)));
- }
-
- return nullptr;
+ return new android::PersistentSurface(
+ inputSurface->getGraphicBufferProducer(),
+ static_cast<android::sp<android::hidl::base::V1_0::IBase>>(
+ inputSurface->getHalInterface()));
}
diff --git a/media/codec2/sfplugin/CCodec.h b/media/codec2/sfplugin/CCodec.h
index bb8bd19..ba5f5f3 100644
--- a/media/codec2/sfplugin/CCodec.h
+++ b/media/codec2/sfplugin/CCodec.h
@@ -89,6 +89,16 @@
void flush();
void release(bool sendCallback);
+ /**
+ * Creates an input surface for the current device configuration compatible with CCodec.
+ * This could be backed by the C2 HAL or the OMX HAL.
+ */
+ static sp<PersistentSurface> CreateCompatibleInputSurface();
+
+ /// Creates an input surface to the OMX HAL
+ static sp<PersistentSurface> CreateOmxInputSurface();
+
+ /// handle a create input surface call
void createInputSurface();
void setInputSurface(const sp<PersistentSurface> &surface);
status_t setupInputSurface(const std::shared_ptr<InputSurfaceWrapper> &surface);
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 587f83c..6842fa5 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -94,6 +94,11 @@
*/
virtual void getArray(Vector<sp<MediaCodecBuffer>> *) const {}
+ /**
+ * Return number of buffers the client owns.
+ */
+ virtual size_t numClientBuffers() const = 0;
+
protected:
std::string mComponentName; ///< name of component for debugging
std::string mChannelName; ///< name of channel for debugging
@@ -152,11 +157,6 @@
*/
virtual std::unique_ptr<InputBuffers> toArrayMode(size_t size) = 0;
- /**
- * Return number of buffers the client owns.
- */
- virtual size_t numClientBuffers() const = 0;
-
protected:
// Pool to obtain blocks for input buffers.
std::shared_ptr<C2BlockPool> mPool;
@@ -1226,6 +1226,10 @@
mImpl.realloc(alloc);
}
+ size_t numClientBuffers() const final {
+ return mImpl.numClientBuffers();
+ }
+
private:
BuffersArrayImpl mImpl;
};
@@ -1287,6 +1291,10 @@
return std::move(array);
}
+ size_t numClientBuffers() const final {
+ return mImpl.numClientBuffers();
+ }
+
/**
* Return an appropriate Codec2Buffer object for the type of buffers.
*
@@ -1816,9 +1824,17 @@
}
void CCodecBufferChannel::feedInputBufferIfAvailableInternal() {
- while (!mInputMetEos &&
- !mReorderStash.lock()->hasPending() &&
- !mPipelineWatcher.lock()->pipelineFull()) {
+ if (mInputMetEos ||
+ mReorderStash.lock()->hasPending() ||
+ mPipelineWatcher.lock()->pipelineFull()) {
+ return;
+ } else {
+ Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
+ if ((*buffers)->numClientBuffers() >= mNumOutputSlots) {
+ return;
+ }
+ }
+ for (size_t i = 0; i < mNumInputSlots; ++i) {
sp<MediaCodecBuffer> inBuffer;
size_t index;
{
diff --git a/media/codec2/sfplugin/Codec2InfoBuilder.cpp b/media/codec2/sfplugin/Codec2InfoBuilder.cpp
index 5f0dd0b..ead0a9b 100644
--- a/media/codec2/sfplugin/Codec2InfoBuilder.cpp
+++ b/media/codec2/sfplugin/Codec2InfoBuilder.cpp
@@ -68,262 +68,146 @@
s.compare(s.size() - suffixLen, suffixLen, suffix) == 0;
}
-// Constants from ACodec
-constexpr OMX_U32 kPortIndexInput = 0;
-constexpr OMX_U32 kPortIndexOutput = 1;
-constexpr OMX_U32 kMaxIndicesToCheck = 32;
+void addSupportedProfileLevels(
+ std::shared_ptr<Codec2Client::Interface> intf,
+ MediaCodecInfo::CapabilitiesWriter *caps,
+ const Traits& trait, const std::string &mediaType) {
+ std::shared_ptr<C2Mapper::ProfileLevelMapper> mapper =
+ C2Mapper::GetProfileLevelMapper(trait.mediaType);
+ // if we don't know the media type, pass through all values unmapped
-status_t queryOmxCapabilities(
- const char* name, const char* mediaType, bool isEncoder,
- MediaCodecInfo::CapabilitiesWriter* caps) {
-
- const char *role = GetComponentRole(isEncoder, mediaType);
- if (role == nullptr) {
- return BAD_VALUE;
- }
-
- using namespace ::android::hardware::media::omx::V1_0;
- using ::android::hardware::Return;
- using ::android::hardware::Void;
- using ::android::hardware::hidl_vec;
- using ::android::hardware::media::omx::V1_0::utils::LWOmxNode;
-
- sp<IOmx> omx = IOmx::getService();
- if (!omx) {
- ALOGW("Could not obtain IOmx service.");
- return NO_INIT;
- }
-
- struct Observer : IOmxObserver {
- virtual Return<void> onMessages(const hidl_vec<Message>&) override {
- return Void();
- }
+ // TODO: we cannot find levels that are local 'maxima' without knowing the coding
+ // e.g. H.263 level 45 and level 30 could be two values for highest level as
+ // they don't include one another. For now we use the last supported value.
+ bool encoder = trait.kind == C2Component::KIND_ENCODER;
+ C2StreamProfileLevelInfo pl(encoder /* output */, 0u);
+ std::vector<C2FieldSupportedValuesQuery> profileQuery = {
+ C2FieldSupportedValuesQuery::Possible(C2ParamField(&pl, &pl.profile))
};
- sp<Observer> observer = new Observer();
- Status status;
- sp<IOmxNode> tOmxNode;
- Return<void> transStatus = omx->allocateNode(
- name, observer,
- [&status, &tOmxNode](Status s, const sp<IOmxNode>& n) {
- status = s;
- tOmxNode = n;
- });
- if (!transStatus.isOk()) {
- ALOGW("IOmx::allocateNode -- transaction failed.");
- return NO_INIT;
- }
- if (status != Status::OK) {
- ALOGW("IOmx::allocateNode -- error returned: %d.",
- static_cast<int>(status));
- return NO_INIT;
+ c2_status_t err = intf->querySupportedValues(profileQuery, C2_DONT_BLOCK);
+ ALOGV("query supported profiles -> %s | %s", asString(err), asString(profileQuery[0].status));
+ if (err != C2_OK || profileQuery[0].status != C2_OK) {
+ return;
}
- sp<LWOmxNode> omxNode = new LWOmxNode(tOmxNode);
-
- status_t err = SetComponentRole(omxNode, role);
- if (err != OK) {
- omxNode->freeNode();
- ALOGW("Failed to SetComponentRole: component = %s, role = %s.",
- name, role);
- return err;
+ // we only handle enumerated values
+ if (profileQuery[0].values.type != C2FieldSupportedValues::VALUES) {
+ return;
}
- bool isVideo = hasPrefix(mediaType, "video/") == 0;
- bool isImage = hasPrefix(mediaType, "image/") == 0;
+ // determine if codec supports HDR
+ bool supportsHdr = false;
+ bool supportsHdr10Plus = false;
- if (isVideo || isImage) {
- OMX_VIDEO_PARAM_PROFILELEVELTYPE param;
- InitOMXParams(¶m);
- param.nPortIndex = isEncoder ? kPortIndexOutput : kPortIndexInput;
-
- for (OMX_U32 index = 0; index <= kMaxIndicesToCheck; ++index) {
- param.nProfileIndex = index;
- status_t err = omxNode->getParameter(
- OMX_IndexParamVideoProfileLevelQuerySupported,
- ¶m, sizeof(param));
- if (err != OK) {
+ std::vector<std::shared_ptr<C2ParamDescriptor>> paramDescs;
+ c2_status_t err1 = intf->querySupportedParams(¶mDescs);
+ if (err1 == C2_OK) {
+ for (const std::shared_ptr<C2ParamDescriptor> &desc : paramDescs) {
+ switch ((uint32_t)desc->index()) {
+ case C2StreamHdr10PlusInfo::output::PARAM_TYPE:
+ supportsHdr10Plus = true;
+ break;
+ case C2StreamHdrStaticInfo::output::PARAM_TYPE:
+ supportsHdr = true;
+ break;
+ default:
break;
}
- caps->addProfileLevel(param.eProfile, param.eLevel);
-
- // AVC components may not list the constrained profiles explicitly, but
- // decoders that support a profile also support its constrained version.
- // Encoders must explicitly support constrained profiles.
- if (!isEncoder && strcasecmp(mediaType, MEDIA_MIMETYPE_VIDEO_AVC) == 0) {
- if (param.eProfile == OMX_VIDEO_AVCProfileHigh) {
- caps->addProfileLevel(OMX_VIDEO_AVCProfileConstrainedHigh, param.eLevel);
- } else if (param.eProfile == OMX_VIDEO_AVCProfileBaseline) {
- caps->addProfileLevel(OMX_VIDEO_AVCProfileConstrainedBaseline, param.eLevel);
- }
- }
-
- if (index == kMaxIndicesToCheck) {
- ALOGW("[%s] stopping checking profiles after %u: %x/%x",
- name, index,
- param.eProfile, param.eLevel);
- }
- }
-
- // Color format query
- // return colors in the order reported by the OMX component
- // prefix "flexible" standard ones with the flexible equivalent
- OMX_VIDEO_PARAM_PORTFORMATTYPE portFormat;
- InitOMXParams(&portFormat);
- portFormat.nPortIndex = isEncoder ? kPortIndexInput : kPortIndexOutput;
- for (OMX_U32 index = 0; index <= kMaxIndicesToCheck; ++index) {
- portFormat.nIndex = index;
- status_t err = omxNode->getParameter(
- OMX_IndexParamVideoPortFormat,
- &portFormat, sizeof(portFormat));
- if (err != OK) {
- break;
- }
-
- OMX_U32 flexibleEquivalent;
- if (IsFlexibleColorFormat(
- omxNode, portFormat.eColorFormat, false /* usingNativeWindow */,
- &flexibleEquivalent)) {
- caps->addColorFormat(flexibleEquivalent);
- }
- caps->addColorFormat(portFormat.eColorFormat);
-
- if (index == kMaxIndicesToCheck) {
- ALOGW("[%s] stopping checking formats after %u: %s(%x)",
- name, index,
- asString(portFormat.eColorFormat), portFormat.eColorFormat);
- }
- }
- } else if (strcasecmp(mediaType, MEDIA_MIMETYPE_AUDIO_AAC) == 0) {
- // More audio codecs if they have profiles.
- OMX_AUDIO_PARAM_ANDROID_PROFILETYPE param;
- InitOMXParams(¶m);
- param.nPortIndex = isEncoder ? kPortIndexOutput : kPortIndexInput;
- for (OMX_U32 index = 0; index <= kMaxIndicesToCheck; ++index) {
- param.nProfileIndex = index;
- status_t err = omxNode->getParameter(
- (OMX_INDEXTYPE)OMX_IndexParamAudioProfileQuerySupported,
- ¶m, sizeof(param));
- if (err != OK) {
- break;
- }
- // For audio, level is ignored.
- caps->addProfileLevel(param.eProfile, 0 /* level */);
-
- if (index == kMaxIndicesToCheck) {
- ALOGW("[%s] stopping checking profiles after %u: %x",
- name, index,
- param.eProfile);
- }
- }
-
- // NOTE: Without Android extensions, OMX does not provide a way to query
- // AAC profile support
- if (param.nProfileIndex == 0) {
- ALOGW("component %s doesn't support profile query.", name);
}
}
- if (isVideo && !isEncoder) {
- native_handle_t *sidebandHandle = nullptr;
- if (omxNode->configureVideoTunnelMode(
- kPortIndexOutput, OMX_TRUE, 0, &sidebandHandle) == OK) {
- // tunneled playback includes adaptive playback
- } else {
- // tunneled playback is not supported
- caps->removeDetail(MediaCodecInfo::Capabilities::FEATURE_TUNNELED_PLAYBACK);
- if (omxNode->setPortMode(
- kPortIndexOutput, IOMX::kPortModeDynamicANWBuffer) == OK ||
- omxNode->prepareForAdaptivePlayback(
- kPortIndexOutput, OMX_TRUE,
- 1280 /* width */, 720 /* height */) != OK) {
- // adaptive playback is not supported
- caps->removeDetail(MediaCodecInfo::Capabilities::FEATURE_ADAPTIVE_PLAYBACK);
- }
- }
- }
+ // For VP9, the static info is always propagated by framework.
+ supportsHdr |= (mediaType == MIMETYPE_VIDEO_VP9);
- if (isVideo && isEncoder) {
- OMX_VIDEO_CONFIG_ANDROID_INTRAREFRESHTYPE params;
- InitOMXParams(¶ms);
- params.nPortIndex = kPortIndexOutput;
-
- OMX_VIDEO_PARAM_INTRAREFRESHTYPE fallbackParams;
- InitOMXParams(&fallbackParams);
- fallbackParams.nPortIndex = kPortIndexOutput;
- fallbackParams.eRefreshMode = OMX_VIDEO_IntraRefreshCyclic;
-
- if (omxNode->getConfig(
- (OMX_INDEXTYPE)OMX_IndexConfigAndroidIntraRefresh,
- ¶ms, sizeof(params)) != OK &&
- omxNode->getParameter(
- OMX_IndexParamVideoIntraRefresh, &fallbackParams,
- sizeof(fallbackParams)) != OK) {
- // intra refresh is not supported
- caps->removeDetail(MediaCodecInfo::Capabilities::FEATURE_INTRA_REFRESH);
- }
- }
-
- omxNode->freeNode();
- return OK;
-}
-
-void buildOmxInfo(const MediaCodecsXmlParser& parser,
- MediaCodecListWriter* writer) {
- uint32_t omxRank = ::android::base::GetUintProperty(
- "debug.stagefright.omx_default_rank", uint32_t(0x100));
- for (const MediaCodecsXmlParser::Codec& codec : parser.getCodecMap()) {
- const std::string &name = codec.first;
- if (!hasPrefix(codec.first, "OMX.")) {
+ for (C2Value::Primitive profile : profileQuery[0].values.values) {
+ pl.profile = (C2Config::profile_t)profile.ref<uint32_t>();
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ err = intf->config({&pl}, C2_DONT_BLOCK, &failures);
+ ALOGV("set profile to %u -> %s", pl.profile, asString(err));
+ std::vector<C2FieldSupportedValuesQuery> levelQuery = {
+ C2FieldSupportedValuesQuery::Current(C2ParamField(&pl, &pl.level))
+ };
+ err = intf->querySupportedValues(levelQuery, C2_DONT_BLOCK);
+ ALOGV("query supported levels -> %s | %s", asString(err), asString(levelQuery[0].status));
+ if (err != C2_OK || levelQuery[0].status != C2_OK
+ || levelQuery[0].values.type != C2FieldSupportedValues::VALUES
+ || levelQuery[0].values.values.size() == 0) {
continue;
}
- const MediaCodecsXmlParser::CodecProperties &properties = codec.second;
- bool encoder = properties.isEncoder;
- std::unique_ptr<MediaCodecInfoWriter> info =
- writer->addMediaCodecInfo();
- info->setName(name.c_str());
- info->setOwner("default");
- typename std::underlying_type<MediaCodecInfo::Attributes>::type attrs = 0;
- if (encoder) {
- attrs |= MediaCodecInfo::kFlagIsEncoder;
- }
- // NOTE: we don't support software-only codecs in OMX
- if (!hasPrefix(name, "OMX.google.")) {
- attrs |= MediaCodecInfo::kFlagIsVendor;
- if (properties.quirkSet.find("attribute::software-codec")
- == properties.quirkSet.end()) {
- attrs |= MediaCodecInfo::kFlagIsHardwareAccelerated;
- }
- }
- info->setAttributes(attrs);
- info->setRank(omxRank);
- // OMX components don't have aliases
- for (const MediaCodecsXmlParser::Type &type : properties.typeMap) {
- const std::string &mediaType = type.first;
- std::unique_ptr<MediaCodecInfo::CapabilitiesWriter> caps =
- info->addMediaType(mediaType.c_str());
- const MediaCodecsXmlParser::AttributeMap &attrMap = type.second;
- for (const MediaCodecsXmlParser::Attribute& attr : attrMap) {
- const std::string &key = attr.first;
- const std::string &value = attr.second;
- if (hasPrefix(key, "feature-") &&
- !hasPrefix(key, "feature-bitrate-modes")) {
- caps->addDetail(key.c_str(), hasPrefix(value, "1") ? 1 : 0);
- } else {
- caps->addDetail(key.c_str(), value.c_str());
+
+ C2Value::Primitive level = levelQuery[0].values.values.back();
+ pl.level = (C2Config::level_t)level.ref<uint32_t>();
+ ALOGV("supporting level: %u", pl.level);
+ int32_t sdkProfile, sdkLevel;
+ if (mapper && mapper->mapProfile(pl.profile, &sdkProfile)
+ && mapper->mapLevel(pl.level, &sdkLevel)) {
+ caps->addProfileLevel((uint32_t)sdkProfile, (uint32_t)sdkLevel);
+ // also list HDR profiles if component supports HDR
+ if (supportsHdr) {
+ auto hdrMapper = C2Mapper::GetHdrProfileLevelMapper(trait.mediaType);
+ if (hdrMapper && hdrMapper->mapProfile(pl.profile, &sdkProfile)) {
+ caps->addProfileLevel((uint32_t)sdkProfile, (uint32_t)sdkLevel);
+ }
+ if (supportsHdr10Plus) {
+ hdrMapper = C2Mapper::GetHdrProfileLevelMapper(
+ trait.mediaType, true /*isHdr10Plus*/);
+ if (hdrMapper && hdrMapper->mapProfile(pl.profile, &sdkProfile)) {
+ caps->addProfileLevel((uint32_t)sdkProfile, (uint32_t)sdkLevel);
+ }
}
}
- status_t err = queryOmxCapabilities(
- name.c_str(),
- mediaType.c_str(),
- encoder,
- caps.get());
- if (err != OK) {
- ALOGI("Failed to query capabilities for %s (media type: %s). Error: %d",
- name.c_str(),
- mediaType.c_str(),
- static_cast<int>(err));
+ } else if (!mapper) {
+ caps->addProfileLevel(pl.profile, pl.level);
+ }
+
+ // for H.263 also advertise the second highest level if the
+ // codec supports level 45, as level 45 only covers level 10
+ // TODO: move this to some form of a setting so it does not
+ // have to be here
+ if (mediaType == MIMETYPE_VIDEO_H263) {
+ C2Config::level_t nextLevel = C2Config::LEVEL_UNUSED;
+ for (C2Value::Primitive v : levelQuery[0].values.values) {
+ C2Config::level_t level = (C2Config::level_t)v.ref<uint32_t>();
+ if (level < C2Config::LEVEL_H263_45 && level > nextLevel) {
+ nextLevel = level;
+ }
}
+ if (nextLevel != C2Config::LEVEL_UNUSED
+ && nextLevel != pl.level
+ && mapper
+ && mapper->mapProfile(pl.profile, &sdkProfile)
+ && mapper->mapLevel(nextLevel, &sdkLevel)) {
+ caps->addProfileLevel(
+ (uint32_t)sdkProfile, (uint32_t)sdkLevel);
+ }
+ }
+ }
+}
+
+void addSupportedColorFormats(
+ std::shared_ptr<Codec2Client::Interface> intf,
+ MediaCodecInfo::CapabilitiesWriter *caps,
+ const Traits& trait, const std::string &mediaType) {
+ (void)intf;
+
+ // TODO: get this from intf() as well, but how do we map them to
+ // MediaCodec color formats?
+ bool encoder = trait.kind == C2Component::KIND_ENCODER;
+ if (mediaType.find("video") != std::string::npos) {
+ // vendor video codecs prefer opaque format
+ if (trait.name.find("android") == std::string::npos) {
+ caps->addColorFormat(COLOR_FormatSurface);
+ }
+ caps->addColorFormat(COLOR_FormatYUV420Flexible);
+ caps->addColorFormat(COLOR_FormatYUV420Planar);
+ caps->addColorFormat(COLOR_FormatYUV420SemiPlanar);
+ caps->addColorFormat(COLOR_FormatYUV420PackedPlanar);
+ caps->addColorFormat(COLOR_FormatYUV420PackedSemiPlanar);
+ // framework video encoders must support surface format, though it is unclear
+ // that they will be able to map it if it is opaque
+ if (encoder && trait.name.find("android") != std::string::npos) {
+ caps->addColorFormat(COLOR_FormatSurface);
}
}
}
@@ -335,7 +219,7 @@
// properly. (Assume "full" behavior eventually.)
//
// debug.stagefright.ccodec supports 5 values.
- // 0 - Only OMX components are available.
+ // 0 - No Codec 2.0 components are available.
// 1 - Audio decoders and encoders with prefix "c2.android." are available
// and ranked first.
// All other components with prefix "c2.android." are available with
@@ -366,306 +250,156 @@
MediaCodecsXmlParser parser(
MediaCodecsXmlParser::defaultSearchDirs,
- option == 0 ? "media_codecs.xml" :
- "media_codecs_c2.xml",
- option == 0 ? "media_codecs_performance.xml" :
- "media_codecs_performance_c2.xml");
+ "media_codecs_c2.xml",
+ "media_codecs_performance_c2.xml");
if (parser.getParsingStatus() != OK) {
ALOGD("XML parser no good");
return OK;
}
- bool surfaceTest(Codec2Client::CreateInputSurface());
- if (option == 0 || (option != 4 && !surfaceTest)) {
- buildOmxInfo(parser, writer);
- }
-
for (const Traits& trait : traits) {
C2Component::rank_t rank = trait.rank;
- std::shared_ptr<Codec2Client::Interface> intf =
- Codec2Client::CreateInterfaceByName(trait.name.c_str());
- if (!intf || parser.getCodecMap().count(intf->getName()) == 0) {
- ALOGD("%s not found in xml", trait.name.c_str());
- continue;
- }
- std::string canonName = intf->getName();
-
- // TODO: Remove this block once all codecs are enabled by default.
- switch (option) {
- case 0:
- continue;
- case 1:
- if (hasPrefix(canonName, "c2.vda.")) {
- break;
+ // Interface must be accessible for us to list the component, and there also
+ // must be an XML entry for the codec. Codec aliases listed in the traits
+ // allow additional XML entries to be specified for each alias. These will
+ // be listed as separate codecs. If no XML entry is specified for an alias,
+ // those will be treated as an additional alias specified in the XML entry
+ // for the interface name.
+ std::vector<std::string> nameAndAliases = trait.aliases;
+ nameAndAliases.insert(nameAndAliases.begin(), trait.name);
+ for (const std::string &nameOrAlias : nameAndAliases) {
+ bool isAlias = trait.name != nameOrAlias;
+ std::shared_ptr<Codec2Client::Interface> intf =
+ Codec2Client::CreateInterfaceByName(nameOrAlias.c_str());
+ if (!intf) {
+ ALOGD("could not create interface for %s'%s'",
+ isAlias ? "alias " : "",
+ nameOrAlias.c_str());
+ continue;
}
- if (hasPrefix(canonName, "c2.android.")) {
- if (trait.domain == C2Component::DOMAIN_AUDIO) {
+ if (parser.getCodecMap().count(nameOrAlias) == 0) {
+ if (isAlias) {
+ std::unique_ptr<MediaCodecInfoWriter> baseCodecInfo =
+ writer->findMediaCodecInfo(trait.name.c_str());
+ if (!baseCodecInfo) {
+ ALOGD("alias '%s' not found in xml but canonical codec info '%s' missing",
+ nameOrAlias.c_str(),
+ trait.name.c_str());
+ } else {
+ ALOGD("alias '%s' not found in xml; use an XML <Alias> tag for this",
+ nameOrAlias.c_str());
+ // merge alias into existing codec
+ baseCodecInfo->addAlias(nameOrAlias.c_str());
+ }
+ } else {
+ ALOGD("component '%s' not found in xml", trait.name.c_str());
+ }
+ continue;
+ }
+ std::string canonName = trait.name;
+
+ // TODO: Remove this block once all codecs are enabled by default.
+ switch (option) {
+ case 0:
+ continue;
+ case 1:
+ if (hasPrefix(canonName, "c2.vda.")) {
+ break;
+ }
+ if (hasPrefix(canonName, "c2.android.")) {
+ if (trait.domain == C2Component::DOMAIN_AUDIO) {
+ rank = 1;
+ break;
+ }
+ break;
+ }
+ if (hasSuffix(canonName, ".avc.decoder") ||
+ hasSuffix(canonName, ".avc.encoder")) {
+ rank = std::numeric_limits<decltype(rank)>::max();
+ break;
+ }
+ continue;
+ case 2:
+ if (hasPrefix(canonName, "c2.vda.")) {
+ break;
+ }
+ if (hasPrefix(canonName, "c2.android.")) {
rank = 1;
break;
}
+ if (hasSuffix(canonName, ".avc.decoder") ||
+ hasSuffix(canonName, ".avc.encoder")) {
+ rank = std::numeric_limits<decltype(rank)>::max();
+ break;
+ }
+ continue;
+ case 3:
+ if (hasPrefix(canonName, "c2.android.")) {
+ rank = 1;
+ }
break;
}
- if (hasSuffix(canonName, ".avc.decoder") ||
- hasSuffix(canonName, ".avc.encoder")) {
- rank = std::numeric_limits<decltype(rank)>::max();
- break;
- }
- continue;
- case 2:
- if (hasPrefix(canonName, "c2.vda.")) {
- break;
- }
- if (hasPrefix(canonName, "c2.android.")) {
- rank = 1;
- break;
- }
- if (hasSuffix(canonName, ".avc.decoder") ||
- hasSuffix(canonName, ".avc.encoder")) {
- rank = std::numeric_limits<decltype(rank)>::max();
- break;
- }
- continue;
- case 3:
- if (hasPrefix(canonName, "c2.android.")) {
- rank = 1;
- }
- break;
- }
- ALOGV("canonName = %s", canonName.c_str());
- std::unique_ptr<MediaCodecInfoWriter> codecInfo = writer->addMediaCodecInfo();
- codecInfo->setName(trait.name.c_str());
- codecInfo->setOwner(("codec2::" + trait.owner).c_str());
- const MediaCodecsXmlParser::CodecProperties &codec = parser.getCodecMap().at(canonName);
+ ALOGV("adding codec entry for '%s'", nameOrAlias.c_str());
+ std::unique_ptr<MediaCodecInfoWriter> codecInfo = writer->addMediaCodecInfo();
+ codecInfo->setName(nameOrAlias.c_str());
+ codecInfo->setOwner(("codec2::" + trait.owner).c_str());
+ const MediaCodecsXmlParser::CodecProperties &codec =
+ parser.getCodecMap().at(nameOrAlias);
- bool encoder = trait.kind == C2Component::KIND_ENCODER;
- typename std::underlying_type<MediaCodecInfo::Attributes>::type attrs = 0;
+ bool encoder = trait.kind == C2Component::KIND_ENCODER;
+ typename std::underlying_type<MediaCodecInfo::Attributes>::type attrs = 0;
- if (encoder) {
- attrs |= MediaCodecInfo::kFlagIsEncoder;
- }
- if (trait.owner == "software") {
- attrs |= MediaCodecInfo::kFlagIsSoftwareOnly;
- } else {
- attrs |= MediaCodecInfo::kFlagIsVendor;
- if (trait.owner == "vendor-software") {
+ if (encoder) {
+ attrs |= MediaCodecInfo::kFlagIsEncoder;
+ }
+ if (trait.owner == "software") {
attrs |= MediaCodecInfo::kFlagIsSoftwareOnly;
- } else if (codec.quirkSet.find("attribute::software-codec") == codec.quirkSet.end()) {
- attrs |= MediaCodecInfo::kFlagIsHardwareAccelerated;
- }
- }
- codecInfo->setAttributes(attrs);
- codecInfo->setRank(rank);
-
- for (const std::string &alias : codec.aliases) {
- codecInfo->addAlias(alias.c_str());
- }
-
- for (auto typeIt = codec.typeMap.begin(); typeIt != codec.typeMap.end(); ++typeIt) {
- const std::string &mediaType = typeIt->first;
- const MediaCodecsXmlParser::AttributeMap &attrMap = typeIt->second;
- std::unique_ptr<MediaCodecInfo::CapabilitiesWriter> caps =
- codecInfo->addMediaType(mediaType.c_str());
- for (auto attrIt = attrMap.begin(); attrIt != attrMap.end(); ++attrIt) {
- std::string key, value;
- std::tie(key, value) = *attrIt;
- if (key.find("feature-") == 0 && key.find("feature-bitrate-modes") != 0) {
- caps->addDetail(key.c_str(), std::stoi(value));
- } else {
- caps->addDetail(key.c_str(), value.c_str());
+ } else {
+ attrs |= MediaCodecInfo::kFlagIsVendor;
+ if (trait.owner == "vendor-software") {
+ attrs |= MediaCodecInfo::kFlagIsSoftwareOnly;
+ } else if (codec.quirkSet.find("attribute::software-codec")
+ == codec.quirkSet.end()) {
+ attrs |= MediaCodecInfo::kFlagIsHardwareAccelerated;
}
}
-
- bool gotProfileLevels = false;
- if (intf) {
- std::shared_ptr<C2Mapper::ProfileLevelMapper> mapper =
- C2Mapper::GetProfileLevelMapper(trait.mediaType);
- // if we don't know the media type, pass through all values unmapped
-
- // TODO: we cannot find levels that are local 'maxima' without knowing the coding
- // e.g. H.263 level 45 and level 30 could be two values for highest level as
- // they don't include one another. For now we use the last supported value.
- C2StreamProfileLevelInfo pl(encoder /* output */, 0u);
- std::vector<C2FieldSupportedValuesQuery> profileQuery = {
- C2FieldSupportedValuesQuery::Possible(C2ParamField(&pl, &pl.profile))
- };
-
- c2_status_t err = intf->querySupportedValues(profileQuery, C2_DONT_BLOCK);
- ALOGV("query supported profiles -> %s | %s",
- asString(err), asString(profileQuery[0].status));
- if (err == C2_OK && profileQuery[0].status == C2_OK) {
- if (profileQuery[0].values.type == C2FieldSupportedValues::VALUES) {
- std::vector<std::shared_ptr<C2ParamDescriptor>> paramDescs;
- c2_status_t err1 = intf->querySupportedParams(¶mDescs);
- bool isHdr = false, isHdr10Plus = false;
- if (err1 == C2_OK) {
- for (const std::shared_ptr<C2ParamDescriptor> &desc : paramDescs) {
- if ((uint32_t)desc->index() ==
- C2StreamHdr10PlusInfo::output::PARAM_TYPE) {
- isHdr10Plus = true;
- } else if ((uint32_t)desc->index() ==
- C2StreamHdrStaticInfo::output::PARAM_TYPE) {
- isHdr = true;
- }
- }
- }
- // For VP9, the static info is always propagated by framework.
- isHdr |= (mediaType == MIMETYPE_VIDEO_VP9);
-
- for (C2Value::Primitive profile : profileQuery[0].values.values) {
- pl.profile = (C2Config::profile_t)profile.ref<uint32_t>();
- std::vector<std::unique_ptr<C2SettingResult>> failures;
- err = intf->config({&pl}, C2_DONT_BLOCK, &failures);
- ALOGV("set profile to %u -> %s", pl.profile, asString(err));
- std::vector<C2FieldSupportedValuesQuery> levelQuery = {
- C2FieldSupportedValuesQuery::Current(C2ParamField(&pl, &pl.level))
- };
- err = intf->querySupportedValues(levelQuery, C2_DONT_BLOCK);
- ALOGV("query supported levels -> %s | %s",
- asString(err), asString(levelQuery[0].status));
- if (err == C2_OK && levelQuery[0].status == C2_OK) {
- if (levelQuery[0].values.type == C2FieldSupportedValues::VALUES
- && levelQuery[0].values.values.size() > 0) {
- C2Value::Primitive level = levelQuery[0].values.values.back();
- pl.level = (C2Config::level_t)level.ref<uint32_t>();
- ALOGV("supporting level: %u", pl.level);
- int32_t sdkProfile, sdkLevel;
- if (mapper && mapper->mapProfile(pl.profile, &sdkProfile)
- && mapper->mapLevel(pl.level, &sdkLevel)) {
- caps->addProfileLevel(
- (uint32_t)sdkProfile, (uint32_t)sdkLevel);
- gotProfileLevels = true;
- if (isHdr) {
- auto hdrMapper = C2Mapper::GetHdrProfileLevelMapper(
- trait.mediaType);
- if (hdrMapper && hdrMapper->mapProfile(
- pl.profile, &sdkProfile)) {
- caps->addProfileLevel(
- (uint32_t)sdkProfile,
- (uint32_t)sdkLevel);
- }
- if (isHdr10Plus) {
- hdrMapper = C2Mapper::GetHdrProfileLevelMapper(
- trait.mediaType, true /*isHdr10Plus*/);
- if (hdrMapper && hdrMapper->mapProfile(
- pl.profile, &sdkProfile)) {
- caps->addProfileLevel(
- (uint32_t)sdkProfile,
- (uint32_t)sdkLevel);
- }
- }
- }
- } else if (!mapper) {
- caps->addProfileLevel(pl.profile, pl.level);
- gotProfileLevels = true;
- }
-
- // for H.263 also advertise the second highest level if the
- // codec supports level 45, as level 45 only covers level 10
- // TODO: move this to some form of a setting so it does not
- // have to be here
- if (mediaType == MIMETYPE_VIDEO_H263) {
- C2Config::level_t nextLevel = C2Config::LEVEL_UNUSED;
- for (C2Value::Primitive v : levelQuery[0].values.values) {
- C2Config::level_t level =
- (C2Config::level_t)v.ref<uint32_t>();
- if (level < C2Config::LEVEL_H263_45
- && level > nextLevel) {
- nextLevel = level;
- }
- }
- if (nextLevel != C2Config::LEVEL_UNUSED
- && nextLevel != pl.level
- && mapper
- && mapper->mapProfile(pl.profile, &sdkProfile)
- && mapper->mapLevel(nextLevel, &sdkLevel)) {
- caps->addProfileLevel(
- (uint32_t)sdkProfile, (uint32_t)sdkLevel);
- }
- }
- }
- }
- }
- }
+ codecInfo->setAttributes(attrs);
+ if (!codec.rank.empty()) {
+ uint32_t xmlRank;
+ char dummy;
+ if (sscanf(codec.rank.c_str(), "%u%c", &xmlRank, &dummy) == 1) {
+ rank = xmlRank;
}
}
+ codecInfo->setRank(rank);
- if (!gotProfileLevels) {
- if (mediaType == MIMETYPE_VIDEO_VP9) {
- if (encoder) {
- caps->addProfileLevel(VP9Profile0, VP9Level41);
- } else {
- caps->addProfileLevel(VP9Profile0, VP9Level5);
- caps->addProfileLevel(VP9Profile2, VP9Level5);
- caps->addProfileLevel(VP9Profile2HDR, VP9Level5);
- }
- } else if (mediaType == MIMETYPE_VIDEO_AV1 && !encoder) {
- caps->addProfileLevel(AV1Profile0, AV1Level2);
- caps->addProfileLevel(AV1Profile0, AV1Level21);
- caps->addProfileLevel(AV1Profile1, AV1Level22);
- caps->addProfileLevel(AV1Profile1, AV1Level3);
- caps->addProfileLevel(AV1Profile2, AV1Level31);
- caps->addProfileLevel(AV1Profile2, AV1Level32);
- } else if (mediaType == MIMETYPE_VIDEO_HEVC && !encoder) {
- caps->addProfileLevel(HEVCProfileMain, HEVCMainTierLevel51);
- caps->addProfileLevel(HEVCProfileMainStill, HEVCMainTierLevel51);
- } else if (mediaType == MIMETYPE_VIDEO_VP8) {
- if (encoder) {
- caps->addProfileLevel(VP8ProfileMain, VP8Level_Version0);
- } else {
- caps->addProfileLevel(VP8ProfileMain, VP8Level_Version0);
- }
- } else if (mediaType == MIMETYPE_VIDEO_AVC) {
- if (encoder) {
- caps->addProfileLevel(AVCProfileBaseline, AVCLevel41);
-// caps->addProfileLevel(AVCProfileConstrainedBaseline, AVCLevel41);
- caps->addProfileLevel(AVCProfileMain, AVCLevel41);
- } else {
- caps->addProfileLevel(AVCProfileBaseline, AVCLevel52);
- caps->addProfileLevel(AVCProfileConstrainedBaseline, AVCLevel52);
- caps->addProfileLevel(AVCProfileMain, AVCLevel52);
- caps->addProfileLevel(AVCProfileConstrainedHigh, AVCLevel52);
- caps->addProfileLevel(AVCProfileHigh, AVCLevel52);
- }
- } else if (mediaType == MIMETYPE_VIDEO_MPEG4) {
- if (encoder) {
- caps->addProfileLevel(MPEG4ProfileSimple, MPEG4Level2);
- } else {
- caps->addProfileLevel(MPEG4ProfileSimple, MPEG4Level3);
- }
- } else if (mediaType == MIMETYPE_VIDEO_H263) {
- if (encoder) {
- caps->addProfileLevel(H263ProfileBaseline, H263Level45);
- } else {
- caps->addProfileLevel(H263ProfileBaseline, H263Level30);
- caps->addProfileLevel(H263ProfileBaseline, H263Level45);
- caps->addProfileLevel(H263ProfileISWV2, H263Level30);
- caps->addProfileLevel(H263ProfileISWV2, H263Level45);
- }
- } else if (mediaType == MIMETYPE_VIDEO_MPEG2 && !encoder) {
- caps->addProfileLevel(MPEG2ProfileSimple, MPEG2LevelHL);
- caps->addProfileLevel(MPEG2ProfileMain, MPEG2LevelHL);
- }
+ for (const std::string &alias : codec.aliases) {
+ ALOGV("adding alias '%s'", alias.c_str());
+ codecInfo->addAlias(alias.c_str());
}
- // TODO: get this from intf() as well, but how do we map them to
- // MediaCodec color formats?
- if (mediaType.find("video") != std::string::npos) {
- // vendor video codecs prefer opaque format
- if (trait.name.find("android") == std::string::npos) {
- caps->addColorFormat(COLOR_FormatSurface);
+ for (auto typeIt = codec.typeMap.begin(); typeIt != codec.typeMap.end(); ++typeIt) {
+ const std::string &mediaType = typeIt->first;
+ const MediaCodecsXmlParser::AttributeMap &attrMap = typeIt->second;
+ std::unique_ptr<MediaCodecInfo::CapabilitiesWriter> caps =
+ codecInfo->addMediaType(mediaType.c_str());
+ for (auto attrIt = attrMap.begin(); attrIt != attrMap.end(); ++attrIt) {
+ std::string key, value;
+ std::tie(key, value) = *attrIt;
+ if (key.find("feature-") == 0 && key.find("feature-bitrate-modes") != 0) {
+ int32_t intValue = 0;
+ // Ignore trailing bad characters and default to 0.
+ (void)sscanf(value.c_str(), "%d", &intValue);
+ caps->addDetail(key.c_str(), intValue);
+ } else {
+ caps->addDetail(key.c_str(), value.c_str());
+ }
}
- caps->addColorFormat(COLOR_FormatYUV420Flexible);
- caps->addColorFormat(COLOR_FormatYUV420Planar);
- caps->addColorFormat(COLOR_FormatYUV420SemiPlanar);
- caps->addColorFormat(COLOR_FormatYUV420PackedPlanar);
- caps->addColorFormat(COLOR_FormatYUV420PackedSemiPlanar);
- // framework video encoders must support surface format, though it is unclear
- // that they will be able to map it if it is opaque
- if (encoder && trait.name.find("android") != std::string::npos) {
- caps->addColorFormat(COLOR_FormatSurface);
- }
+
+ addSupportedProfileLevels(intf, caps.get(), trait, mediaType);
+ addSupportedColorFormats(intf, caps.get(), trait, mediaType);
}
}
}
@@ -677,4 +411,3 @@
extern "C" android::MediaCodecListBuilderBase *CreateBuilder() {
return new android::Codec2InfoBuilder;
}
-
diff --git a/media/codec2/vndk/C2Store.cpp b/media/codec2/vndk/C2Store.cpp
index dc7e89c..f07d9b0 100644
--- a/media/codec2/vndk/C2Store.cpp
+++ b/media/codec2/vndk/C2Store.cpp
@@ -517,7 +517,6 @@
*
* \note Only used by ComponentLoader.
*
- * \param alias[in] module alias
* \param libPath[in] library path
*
* \retval C2_OK the component module has been successfully loaded
@@ -527,7 +526,7 @@
* \retval C2_REFUSED permission denied to load the component module (unexpected)
* \retval C2_TIMED_OUT could not load the module within the time limit (unexpected)
*/
- c2_status_t init(std::string alias, std::string libPath);
+ c2_status_t init(std::string libPath);
virtual ~ComponentModule() override;
@@ -570,7 +569,7 @@
std::shared_ptr<ComponentModule> localModule = mModule.lock();
if (localModule == nullptr) {
localModule = std::make_shared<ComponentModule>();
- res = localModule->init(mAlias, mLibPath);
+ res = localModule->init(mLibPath);
if (res == C2_OK) {
mModule = localModule;
}
@@ -582,13 +581,12 @@
/**
* Creates a component loader for a specific library path (or name).
*/
- ComponentLoader(std::string alias, std::string libPath)
- : mAlias(alias), mLibPath(libPath) {}
+ ComponentLoader(std::string libPath)
+ : mLibPath(libPath) {}
private:
std::mutex mMutex; ///< mutex guarding the module
std::weak_ptr<ComponentModule> mModule; ///< weak reference to the loaded module
- std::string mAlias; ///< component alias
std::string mLibPath; ///< library path
};
@@ -624,9 +622,10 @@
};
/**
- * Retrieves the component loader for a component.
+ * Retrieves the component module for a component.
*
- * \return a non-ref-holding pointer to the component loader.
+ * \param module pointer to a shared_pointer where the component module will be stored on
+ * success.
*
* \retval C2_OK the component loader has been successfully retrieved
* \retval C2_NO_MEMORY not enough memory to locate the component loader
@@ -640,16 +639,25 @@
* component but some components could not be loaded due to lack of
* permissions)
*/
- c2_status_t findComponent(C2String name, ComponentLoader **loader);
+ c2_status_t findComponent(C2String name, std::shared_ptr<ComponentModule> *module);
- std::map<C2String, ComponentLoader> mComponents; ///< map of name -> components
- std::vector<C2String> mComponentsList; ///< list of components
+ /**
+ * Loads each component module and discover its contents.
+ */
+ void visitComponents();
+
+ std::mutex mMutex; ///< mutex guarding the component lists during construction
+ bool mVisited; ///< component modules visited
+ std::map<C2String, ComponentLoader> mComponents; ///< path -> component module
+ std::map<C2String, C2String> mComponentNameToPath; ///< name -> path
+ std::vector<std::shared_ptr<const C2Component::Traits>> mComponentList;
+
std::shared_ptr<C2ReflectorHelper> mReflector;
Interface mInterface;
};
c2_status_t C2PlatformComponentStore::ComponentModule::init(
- std::string alias, std::string libPath) {
+ std::string libPath) {
ALOGV("in %s", __func__);
ALOGV("loading dll");
mLibHandle = dlopen(libPath.c_str(), RTLD_NOW|RTLD_NODELETE);
@@ -684,13 +692,27 @@
std::shared_ptr<C2Component::Traits> traits(new (std::nothrow) C2Component::Traits);
if (traits) {
- if (alias != intf->getName()) {
- ALOGV("%s is alias to %s", alias.c_str(), intf->getName().c_str());
+ traits->name = intf->getName();
+
+ C2ComponentKindSetting kind;
+ C2ComponentDomainSetting domain;
+ res = intf->query_vb({ &kind, &domain }, {}, C2_MAY_BLOCK, nullptr);
+ bool fixDomain = res != C2_OK;
+ if (res == C2_OK) {
+ traits->kind = kind.value;
+ traits->domain = domain.value;
+ } else {
+ // TODO: remove this fall-back
+ ALOGD("failed to query interface for kind and domain: %d", res);
+
+ traits->kind =
+ (traits->name.find("encoder") != std::string::npos) ? C2Component::KIND_ENCODER :
+ (traits->name.find("decoder") != std::string::npos) ? C2Component::KIND_DECODER :
+ C2Component::KIND_OTHER;
}
- traits->name = alias;
- // TODO: get this from interface properly.
- bool encoder = (traits->name.find("encoder") != std::string::npos);
- uint32_t mediaTypeIndex = encoder ? C2PortMimeConfig::output::PARAM_TYPE
+
+ uint32_t mediaTypeIndex =
+ traits->kind == C2Component::KIND_ENCODER ? C2PortMimeConfig::output::PARAM_TYPE
: C2PortMimeConfig::input::PARAM_TYPE;
std::vector<std::unique_ptr<C2Param>> params;
res = intf->query_vb({}, { mediaTypeIndex }, C2_MAY_BLOCK, ¶ms);
@@ -702,29 +724,54 @@
ALOGD("failed to query interface: unexpected vector size: %zu", params.size());
return mInit;
}
- C2PortMimeConfig *mediaTypeConfig = (C2PortMimeConfig *)(params[0].get());
+ C2PortMimeConfig *mediaTypeConfig = C2PortMimeConfig::From(params[0].get());
if (mediaTypeConfig == nullptr) {
ALOGD("failed to query media type");
return mInit;
}
- traits->mediaType = mediaTypeConfig->m.value;
- // TODO: get this properly.
- traits->rank = 0x200;
+ traits->mediaType =
+ std::string(mediaTypeConfig->m.value,
+ strnlen(mediaTypeConfig->m.value, mediaTypeConfig->flexCount()));
- // TODO: define these values properly
- bool decoder = (traits->name.find("decoder") != std::string::npos);
- traits->kind =
- decoder ? C2Component::KIND_DECODER :
- encoder ? C2Component::KIND_ENCODER :
- C2Component::KIND_OTHER;
- if (strncmp(traits->mediaType.c_str(), "audio/", 6) == 0) {
- traits->domain = C2Component::DOMAIN_AUDIO;
- } else if (strncmp(traits->mediaType.c_str(), "video/", 6) == 0) {
- traits->domain = C2Component::DOMAIN_VIDEO;
- } else if (strncmp(traits->mediaType.c_str(), "image/", 6) == 0) {
- traits->domain = C2Component::DOMAIN_IMAGE;
- } else {
- traits->domain = C2Component::DOMAIN_OTHER;
+ if (fixDomain) {
+ if (strncmp(traits->mediaType.c_str(), "audio/", 6) == 0) {
+ traits->domain = C2Component::DOMAIN_AUDIO;
+ } else if (strncmp(traits->mediaType.c_str(), "video/", 6) == 0) {
+ traits->domain = C2Component::DOMAIN_VIDEO;
+ } else if (strncmp(traits->mediaType.c_str(), "image/", 6) == 0) {
+ traits->domain = C2Component::DOMAIN_IMAGE;
+ } else {
+ traits->domain = C2Component::DOMAIN_OTHER;
+ }
+ }
+
+ // TODO: get this properly from the store during emplace
+ switch (traits->domain) {
+ case C2Component::DOMAIN_AUDIO:
+ traits->rank = 8;
+ break;
+ default:
+ traits->rank = 512;
+ }
+
+ params.clear();
+ res = intf->query_vb({}, { C2ComponentAliasesSetting::PARAM_TYPE }, C2_MAY_BLOCK, ¶ms);
+ if (res == C2_OK && params.size() == 1u) {
+ C2ComponentAliasesSetting *aliasesSetting =
+ C2ComponentAliasesSetting::From(params[0].get());
+ if (aliasesSetting) {
+ // Split aliases on ','
+ // This looks simpler in plain C and even std::string would still make a copy.
+ char *aliases = ::strndup(aliasesSetting->m.value, aliasesSetting->flexCount());
+ ALOGD("'%s' has aliases: '%s'", intf->getName().c_str(), aliases);
+
+ for (char *tok, *ptr, *str = aliases; (tok = ::strtok_r(str, ",", &ptr));
+ str = nullptr) {
+ traits->aliases.push_back(tok);
+ ALOGD("adding alias: '%s'", tok);
+ }
+ free(aliases);
+ }
}
}
mTraits = traits;
@@ -783,82 +830,45 @@
}
C2PlatformComponentStore::C2PlatformComponentStore()
- : mReflector(std::make_shared<C2ReflectorHelper>()),
+ : mVisited(false),
+ mReflector(std::make_shared<C2ReflectorHelper>()),
mInterface(mReflector) {
- auto emplace = [this](const char *alias, const char *libPath) {
- // ComponentLoader is neither copiable nor movable, so it must be
- // constructed in-place. Now ComponentLoader takes two arguments in
- // constructor, so we need to use piecewise_construct to achieve this
- // behavior.
- mComponents.emplace(
- std::piecewise_construct,
- std::forward_as_tuple(alias),
- std::forward_as_tuple(alias, libPath));
- mComponentsList.emplace_back(alias);
+ auto emplace = [this](const char *libPath) {
+ mComponents.emplace(libPath, libPath);
};
- // TODO: move this also into a .so so it can be updated
- emplace("c2.android.avc.decoder", "libcodec2_soft_avcdec.so");
- emplace("c2.android.avc.encoder", "libcodec2_soft_avcenc.so");
- emplace("c2.android.aac.decoder", "libcodec2_soft_aacdec.so");
- emplace("c2.android.aac.encoder", "libcodec2_soft_aacenc.so");
- emplace("c2.android.amrnb.decoder", "libcodec2_soft_amrnbdec.so");
- emplace("c2.android.amrnb.encoder", "libcodec2_soft_amrnbenc.so");
- emplace("c2.android.amrwb.decoder", "libcodec2_soft_amrwbdec.so");
- emplace("c2.android.amrwb.encoder", "libcodec2_soft_amrwbenc.so");
- emplace("c2.android.hevc.decoder", "libcodec2_soft_hevcdec.so");
- emplace("c2.android.g711.alaw.decoder", "libcodec2_soft_g711alawdec.so");
- emplace("c2.android.g711.mlaw.decoder", "libcodec2_soft_g711mlawdec.so");
- emplace("c2.android.mpeg2.decoder", "libcodec2_soft_mpeg2dec.so");
- emplace("c2.android.h263.decoder", "libcodec2_soft_h263dec.so");
- emplace("c2.android.h263.encoder", "libcodec2_soft_h263enc.so");
- emplace("c2.android.mpeg4.decoder", "libcodec2_soft_mpeg4dec.so");
- emplace("c2.android.mpeg4.encoder", "libcodec2_soft_mpeg4enc.so");
- emplace("c2.android.mp3.decoder", "libcodec2_soft_mp3dec.so");
- emplace("c2.android.vorbis.decoder", "libcodec2_soft_vorbisdec.so");
- emplace("c2.android.opus.decoder", "libcodec2_soft_opusdec.so");
- emplace("c2.android.opus.encoder", "libcodec2_soft_opusenc.so");
- emplace("c2.android.vp8.decoder", "libcodec2_soft_vp8dec.so");
- emplace("c2.android.vp9.decoder", "libcodec2_soft_vp9dec.so");
- emplace("c2.android.vp8.encoder", "libcodec2_soft_vp8enc.so");
- emplace("c2.android.vp9.encoder", "libcodec2_soft_vp9enc.so");
- emplace("c2.android.av1.decoder", "libcodec2_soft_av1dec.so");
- emplace("c2.android.raw.decoder", "libcodec2_soft_rawdec.so");
- emplace("c2.android.flac.decoder", "libcodec2_soft_flacdec.so");
- emplace("c2.android.flac.encoder", "libcodec2_soft_flacenc.so");
- emplace("c2.android.gsm.decoder", "libcodec2_soft_gsmdec.so");
- emplace("c2.android.xaac.decoder", "libcodec2_soft_xaacdec.so");
- // "Aliases"
- // TODO: use aliases proper from C2Component::Traits
- emplace("OMX.google.h264.decoder", "libcodec2_soft_avcdec.so");
- emplace("OMX.google.h264.encoder", "libcodec2_soft_avcenc.so");
- emplace("OMX.google.aac.decoder", "libcodec2_soft_aacdec.so");
- emplace("OMX.google.aac.encoder", "libcodec2_soft_aacenc.so");
- emplace("OMX.google.amrnb.decoder", "libcodec2_soft_amrnbdec.so");
- emplace("OMX.google.amrnb.encoder", "libcodec2_soft_amrnbenc.so");
- emplace("OMX.google.amrwb.decoder", "libcodec2_soft_amrwbdec.so");
- emplace("OMX.google.amrwb.encoder", "libcodec2_soft_amrwbenc.so");
- emplace("OMX.google.hevc.decoder", "libcodec2_soft_hevcdec.so");
- emplace("OMX.google.g711.alaw.decoder", "libcodec2_soft_g711alawdec.so");
- emplace("OMX.google.g711.mlaw.decoder", "libcodec2_soft_g711mlawdec.so");
- emplace("OMX.google.mpeg2.decoder", "libcodec2_soft_mpeg2dec.so");
- emplace("OMX.google.h263.decoder", "libcodec2_soft_h263dec.so");
- emplace("OMX.google.h263.encoder", "libcodec2_soft_h263enc.so");
- emplace("OMX.google.mpeg4.decoder", "libcodec2_soft_mpeg4dec.so");
- emplace("OMX.google.mpeg4.encoder", "libcodec2_soft_mpeg4enc.so");
- emplace("OMX.google.mp3.decoder", "libcodec2_soft_mp3dec.so");
- emplace("OMX.google.vorbis.decoder", "libcodec2_soft_vorbisdec.so");
- emplace("OMX.google.opus.decoder", "libcodec2_soft_opusdec.so");
- emplace("OMX.google.vp8.decoder", "libcodec2_soft_vp8dec.so");
- emplace("OMX.google.vp9.decoder", "libcodec2_soft_vp9dec.so");
- emplace("OMX.google.vp8.encoder", "libcodec2_soft_vp8enc.so");
- emplace("OMX.google.vp9.encoder", "libcodec2_soft_vp9enc.so");
- emplace("OMX.google.raw.decoder", "libcodec2_soft_rawdec.so");
- emplace("OMX.google.flac.decoder", "libcodec2_soft_flacdec.so");
- emplace("OMX.google.flac.encoder", "libcodec2_soft_flacenc.so");
- emplace("OMX.google.gsm.decoder", "libcodec2_soft_gsmdec.so");
- emplace("OMX.google.xaac.decoder", "libcodec2_soft_xaacdec.so");
+ // TODO: move this also into a .so so it can be updated
+ emplace("libcodec2_soft_aacdec.so");
+ emplace("libcodec2_soft_aacenc.so");
+ emplace("libcodec2_soft_amrnbdec.so");
+ emplace("libcodec2_soft_amrnbenc.so");
+ emplace("libcodec2_soft_amrwbdec.so");
+ emplace("libcodec2_soft_amrwbenc.so");
+ emplace("libcodec2_soft_av1dec.so");
+ emplace("libcodec2_soft_avcdec.so");
+ emplace("libcodec2_soft_avcenc.so");
+ emplace("libcodec2_soft_flacdec.so");
+ emplace("libcodec2_soft_flacenc.so");
+ emplace("libcodec2_soft_g711alawdec.so");
+ emplace("libcodec2_soft_g711mlawdec.so");
+ emplace("libcodec2_soft_gsmdec.so");
+ emplace("libcodec2_soft_h263dec.so");
+ emplace("libcodec2_soft_h263enc.so");
+ emplace("libcodec2_soft_hevcdec.so");
+ emplace("libcodec2_soft_mp3dec.so");
+ emplace("libcodec2_soft_mpeg2dec.so");
+ emplace("libcodec2_soft_mpeg4dec.so");
+ emplace("libcodec2_soft_mpeg4enc.so");
+ emplace("libcodec2_soft_opusdec.so");
+ emplace("libcodec2_soft_opusenc.so");
+ emplace("libcodec2_soft_rawdec.so");
+ emplace("libcodec2_soft_vorbisdec.so");
+ emplace("libcodec2_soft_vp8dec.so");
+ emplace("libcodec2_soft_vp8enc.so");
+ emplace("libcodec2_soft_vp9dec.so");
+ emplace("libcodec2_soft_vp9enc.so");
+ emplace("libcodec2_soft_xaacdec.so");
}
c2_status_t C2PlatformComponentStore::copyBuffer(
@@ -881,47 +891,56 @@
return mInterface.config(params, C2_MAY_BLOCK, failures);
}
-std::vector<std::shared_ptr<const C2Component::Traits>> C2PlatformComponentStore::listComponents() {
- // This method SHALL return within 500ms.
- std::vector<std::shared_ptr<const C2Component::Traits>> list;
- for (const C2String &alias : mComponentsList) {
- ComponentLoader &loader = mComponents.at(alias);
+void C2PlatformComponentStore::visitComponents() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mVisited) {
+ return;
+ }
+ for (auto &pathAndLoader : mComponents) {
+ const C2String &path = pathAndLoader.first;
+ ComponentLoader &loader = pathAndLoader.second;
std::shared_ptr<ComponentModule> module;
- c2_status_t res = loader.fetchModule(&module);
- if (res == C2_OK) {
+ if (loader.fetchModule(&module) == C2_OK) {
std::shared_ptr<const C2Component::Traits> traits = module->getTraits();
if (traits) {
- list.push_back(traits);
+ mComponentList.push_back(traits);
+ mComponentNameToPath.emplace(traits->name, path);
+ for (const C2String &alias : traits->aliases) {
+ mComponentNameToPath.emplace(alias, path);
+ }
}
}
}
- return list;
+ mVisited = true;
}
-c2_status_t C2PlatformComponentStore::findComponent(C2String name, ComponentLoader **loader) {
- *loader = nullptr;
- auto pos = mComponents.find(name);
- // TODO: check aliases
- if (pos == mComponents.end()) {
- return C2_NOT_FOUND;
+std::vector<std::shared_ptr<const C2Component::Traits>> C2PlatformComponentStore::listComponents() {
+ // This method SHALL return within 500ms.
+ visitComponents();
+ return mComponentList;
+}
+
+c2_status_t C2PlatformComponentStore::findComponent(
+ C2String name, std::shared_ptr<ComponentModule> *module) {
+ (*module).reset();
+ visitComponents();
+
+ auto pos = mComponentNameToPath.find(name);
+ if (pos != mComponentNameToPath.end()) {
+ return mComponents.at(pos->second).fetchModule(module);
}
- *loader = &pos->second;
- return C2_OK;
+ return C2_NOT_FOUND;
}
c2_status_t C2PlatformComponentStore::createComponent(
C2String name, std::shared_ptr<C2Component> *const component) {
// This method SHALL return within 100ms.
component->reset();
- ComponentLoader *loader;
- c2_status_t res = findComponent(name, &loader);
+ std::shared_ptr<ComponentModule> module;
+ c2_status_t res = findComponent(name, &module);
if (res == C2_OK) {
- std::shared_ptr<ComponentModule> module;
- res = loader->fetchModule(&module);
- if (res == C2_OK) {
- // TODO: get a unique node ID
- res = module->createComponent(0, component);
- }
+ // TODO: get a unique node ID
+ res = module->createComponent(0, component);
}
return res;
}
@@ -930,15 +949,11 @@
C2String name, std::shared_ptr<C2ComponentInterface> *const interface) {
// This method SHALL return within 100ms.
interface->reset();
- ComponentLoader *loader;
- c2_status_t res = findComponent(name, &loader);
+ std::shared_ptr<ComponentModule> module;
+ c2_status_t res = findComponent(name, &module);
if (res == C2_OK) {
- std::shared_ptr<ComponentModule> module;
- res = loader->fetchModule(&module);
- if (res == C2_OK) {
- // TODO: get a unique node ID
- res = module->createInterface(0, interface);
- }
+ // TODO: get a unique node ID
+ res = module->createInterface(0, interface);
}
return res;
}
diff --git a/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h b/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h
index 9711b86..8eb70b1 100644
--- a/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h
+++ b/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h
@@ -310,7 +310,7 @@
}
// Write SHORT data from the first channel.
- int write(int16_t *inputData, int inputChannelCount, int numFrames) {
+ int32_t write(int16_t *inputData, int32_t inputChannelCount, int32_t numFrames) {
// stop at end of buffer
if ((mFrameCounter + numFrames) > mMaxFrames) {
numFrames = mMaxFrames - mFrameCounter;
@@ -322,7 +322,7 @@
}
// Write FLOAT data from the first channel.
- int write(float *inputData, int inputChannelCount, int numFrames) {
+ int32_t write(float *inputData, int32_t inputChannelCount, int32_t numFrames) {
// stop at end of buffer
if ((mFrameCounter + numFrames) > mMaxFrames) {
numFrames = mMaxFrames - mFrameCounter;
@@ -333,7 +333,7 @@
return numFrames;
}
- int size() {
+ int32_t size() {
return mFrameCounter;
}
@@ -443,9 +443,14 @@
virtual ~LoopbackProcessor() = default;
+ enum process_result {
+ PROCESS_RESULT_OK,
+ PROCESS_RESULT_GLITCH
+ };
+
virtual void reset() {}
- virtual void process(float *inputData, int inputChannelCount,
+ virtual process_result process(float *inputData, int inputChannelCount,
float *outputData, int outputChannelCount,
int numFrames) = 0;
@@ -639,7 +644,7 @@
return getSampleRate() / 8;
}
- void process(float *inputData, int inputChannelCount,
+ process_result process(float *inputData, int inputChannelCount,
float *outputData, int outputChannelCount,
int numFrames) override {
int channelsValid = std::min(inputChannelCount, outputChannelCount);
@@ -750,6 +755,7 @@
mState = nextState;
mLoopCounter++;
+ return PROCESS_RESULT_OK;
}
int save(const char *fileName) override {
@@ -896,9 +902,10 @@
* @param inputData contains microphone data with sine signal feedback
* @param outputData contains the reference sine wave
*/
- void process(float *inputData, int inputChannelCount,
+ process_result process(float *inputData, int inputChannelCount,
float *outputData, int outputChannelCount,
int numFrames) override {
+ process_result result = PROCESS_RESULT_OK;
mProcessCount++;
float peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames);
@@ -978,6 +985,7 @@
mMaxGlitchDelta = std::max(mMaxGlitchDelta, absDiff);
if (absDiff > mTolerance) {
mGlitchCount++;
+ result = PROCESS_RESULT_GLITCH;
//printf("%5d: Got a glitch # %d, predicted = %f, actual = %f\n",
// mFrameCounter, mGlitchCount, predicted, sample);
mState = STATE_IMMUNE;
@@ -1018,6 +1026,7 @@
mFrameCounter++;
}
+ return result;
}
void resetAccumulator() {
diff --git a/media/libaaudio/examples/loopback/src/loopback.cpp b/media/libaaudio/examples/loopback/src/loopback.cpp
index 3de1514..75d425f 100644
--- a/media/libaaudio/examples/loopback/src/loopback.cpp
+++ b/media/libaaudio/examples/loopback/src/loopback.cpp
@@ -34,6 +34,7 @@
#include "AAudioSimpleRecorder.h"
#include "AAudioExampleUtils.h"
#include "LoopbackAnalyzer.h"
+#include "../../utils/AAudioExampleUtils.h"
// V0.4.00 = rectify and low-pass filter the echos, use auto-correlation on entire echo
#define APP_VERSION "0.4.00"
@@ -47,10 +48,14 @@
constexpr int kLogPeriodMillis = 1000;
constexpr int kNumInputChannels = 1;
constexpr int kNumCallbacksToDrain = 20;
+constexpr int kNumCallbacksToNotRead = 0; // let input fill back up
constexpr int kNumCallbacksToDiscard = 20;
+constexpr int kDefaultHangTimeMillis = 50;
+constexpr int kMaxGlitchEventsToSave = 32;
struct LoopbackData {
AAudioStream *inputStream = nullptr;
+ AAudioStream *outputStream = nullptr;
int32_t inputFramesMaximum = 0;
int16_t *inputShortData = nullptr;
float *inputFloatData = nullptr;
@@ -58,6 +63,7 @@
int32_t actualInputChannelCount = 0;
int32_t actualOutputChannelCount = 0;
int32_t numCallbacksToDrain = kNumCallbacksToDrain;
+ int32_t numCallbacksToNotRead = kNumCallbacksToNotRead;
int32_t numCallbacksToDiscard = kNumCallbacksToDiscard;
int32_t minNumFrames = INT32_MAX;
int32_t maxNumFrames = 0;
@@ -65,6 +71,9 @@
int32_t insufficientReadFrames = 0;
int32_t framesReadTotal = 0;
int32_t framesWrittenTotal = 0;
+ int32_t hangPeriodMillis = 5 * 1000; // time between hangs
+ int32_t hangCountdownFrames = 5 * 48000; // frames til next hang
+ int32_t hangTimeMillis = 0; // 0 for no hang
bool isDone = false;
aaudio_result_t inputError = AAUDIO_OK;
@@ -74,6 +83,29 @@
EchoAnalyzer echoAnalyzer;
AudioRecording audioRecording;
LoopbackProcessor *loopbackProcessor;
+
+ int32_t glitchFrames[kMaxGlitchEventsToSave];
+ int32_t numGlitchEvents = 0;
+
+ void hangIfRequested(int32_t numFrames) {
+ if (hangTimeMillis > 0) {
+ hangCountdownFrames -= numFrames;
+ if (hangCountdownFrames <= 0) {
+ const int64_t startNanos = getNanoseconds();
+ usleep(hangTimeMillis * 1000);
+ const int64_t endNanos = getNanoseconds();
+ const int32_t elapsedMicros = (int32_t)
+ ((endNanos - startNanos) / 1000);
+ printf("callback hanging for %d millis, actual = %d micros\n",
+ hangTimeMillis, elapsedMicros);
+ hangCountdownFrames = (int64_t) hangPeriodMillis
+ * AAudioStream_getSampleRate(outputStream)
+ / 1000;
+ }
+ }
+
+
+ }
};
static void convertPcm16ToFloat(const int16_t *source,
@@ -166,6 +198,9 @@
myData->numCallbacksToDrain--;
}
+ } else if (myData->numCallbacksToNotRead > 0) {
+ // Let the input fill up a bit so we are not so close to the write pointer.
+ myData->numCallbacksToNotRead--;
} else if (myData->numCallbacksToDiscard > 0) {
// Ignore. Allow the input to fill back up to equilibrium with the output.
actualFramesRead = readFormattedData(myData, numFrames);
@@ -175,6 +210,7 @@
myData->numCallbacksToDiscard--;
} else {
+ myData->hangIfRequested(numFrames);
int32_t numInputBytes = numFrames * myData->actualInputChannelCount * sizeof(float);
memset(myData->inputFloatData, 0 /* value */, numInputBytes);
@@ -191,7 +227,7 @@
if (actualFramesRead < numFrames) {
if(actualFramesRead < (int32_t) framesAvailable) {
- printf("insufficient but numFrames = %d"
+ printf("insufficient for no reason, numFrames = %d"
", actualFramesRead = %d"
", inputFramesWritten = %d"
", inputFramesRead = %d"
@@ -212,16 +248,25 @@
if (myData->actualInputFormat == AAUDIO_FORMAT_PCM_I16) {
convertPcm16ToFloat(myData->inputShortData, myData->inputFloatData, numSamples);
}
- // Save for later.
- myData->audioRecording.write(myData->inputFloatData,
- myData->actualInputChannelCount,
- numFrames);
+
// Analyze the data.
- myData->loopbackProcessor->process(myData->inputFloatData,
+ LoopbackProcessor::process_result procResult = myData->loopbackProcessor->process(myData->inputFloatData,
myData->actualInputChannelCount,
outputData,
myData->actualOutputChannelCount,
numFrames);
+
+ if (procResult == LoopbackProcessor::PROCESS_RESULT_GLITCH) {
+ if (myData->numGlitchEvents < kMaxGlitchEventsToSave) {
+ myData->glitchFrames[myData->numGlitchEvents++] = myData->audioRecording.size();
+ }
+ }
+
+ // Save for later.
+ myData->audioRecording.write(myData->inputFloatData,
+ myData->actualInputChannelCount,
+ actualFramesRead);
+
myData->isDone = myData->loopbackProcessor->isDone();
if (myData->isDone) {
result = AAUDIO_CALLBACK_RESULT_STOP;
@@ -249,6 +294,7 @@
printf(" -C{channels} number of input channels\n");
printf(" -F{0,1,2} input format, 1=I16, 2=FLOAT\n");
printf(" -g{gain} recirculating loopback gain\n");
+ printf(" -h{hangMillis} occasionally hang in the callback\n");
printf(" -P{inPerf} set input AAUDIO_PERFORMANCE_MODE*\n");
printf(" n for _NONE\n");
printf(" l for _LATENCY\n");
@@ -307,9 +353,7 @@
return testMode;
}
-void printAudioGraph(AudioRecording &recording, int numSamples) {
- int32_t start = recording.size() / 2;
- int32_t end = start + numSamples;
+void printAudioGraphRegion(AudioRecording &recording, int32_t start, int32_t end) {
if (end >= recording.size()) {
end = recording.size() - 1;
}
@@ -360,6 +404,7 @@
int testMode = TEST_ECHO_LATENCY;
double gain = 1.0;
+ int hangTimeMillis = 0;
// Make printf print immediately so that debug info is not stuck
// in a buffer if we hang or crash.
@@ -389,6 +434,15 @@
case 'g':
gain = atof(&arg[2]);
break;
+ case 'h':
+ // Was there a number after the "-h"?
+ if (arg[2]) {
+ hangTimeMillis = atoi(&arg[2]);
+ } else {
+ // If no number then use the default.
+ hangTimeMillis = kDefaultHangTimeMillis;
+ }
+ break;
case 'P':
inputPerformanceLevel = parsePerformanceMode(arg[2]);
break;
@@ -453,7 +507,7 @@
fprintf(stderr, "ERROR - player.open() returned %d\n", result);
exit(1);
}
- outputStream = player.getStream();
+ outputStream = loopbackData.outputStream = player.getStream();
actualOutputFormat = AAudioStream_getFormat(outputStream);
if (actualOutputFormat != AAUDIO_FORMAT_PCM_FLOAT) {
@@ -487,20 +541,24 @@
}
inputStream = loopbackData.inputStream = recorder.getStream();
- {
- int32_t actualCapacity = AAudioStream_getBufferCapacityInFrames(inputStream);
- result = AAudioStream_setBufferSizeInFrames(inputStream, actualCapacity);
- if (result < 0) {
- fprintf(stderr, "ERROR - AAudioStream_setBufferSizeInFrames() returned %d\n", result);
- goto finish;
- } else {}
- }
-
argParser.compareWithStream(inputStream);
- // If the input stream is too small then we cannot satisfy the output callback.
{
int32_t actualCapacity = AAudioStream_getBufferCapacityInFrames(inputStream);
+ (void) AAudioStream_setBufferSizeInFrames(inputStream, actualCapacity);
+
+ if (testMode == TEST_SINE_MAGNITUDE) {
+ result = AAudioStream_setBufferSizeInFrames(outputStream, actualCapacity);
+ if (result < 0) {
+ fprintf(stderr, "ERROR - AAudioStream_setBufferSizeInFrames(output) returned %d\n",
+ result);
+ goto finish;
+ } else {
+ printf("Output buffer size set to match input capacity = %d frames.\n", result);
+ }
+ }
+
+ // If the input stream is too small then we cannot satisfy the output callback.
if (actualCapacity < 2 * outputFramesPerBurst) {
fprintf(stderr, "ERROR - input capacity < 2 * outputFramesPerBurst\n");
goto finish;
@@ -525,6 +583,8 @@
loopbackData.loopbackProcessor->reset();
+ loopbackData.hangTimeMillis = hangTimeMillis;
+
// Start OUTPUT first so INPUT does not overflow.
result = player.start();
if (result != AAUDIO_OK) {
@@ -611,7 +671,17 @@
if (loopbackData.inputError == AAUDIO_OK) {
if (testMode == TEST_SINE_MAGNITUDE) {
- printAudioGraph(loopbackData.audioRecording, 200);
+ if (loopbackData.numGlitchEvents > 0) {
+ // Graph around the first glitch if there is one.
+ const int32_t start = loopbackData.glitchFrames[0] - 8;
+ const int32_t end = start + outputFramesPerBurst + 8 + 8;
+ printAudioGraphRegion(loopbackData.audioRecording, start, end);
+ } else {
+ // Or graph the middle of the signal.
+ const int32_t start = loopbackData.audioRecording.size() / 2;
+ const int32_t end = start + 200;
+ printAudioGraphRegion(loopbackData.audioRecording, start, end);
+ }
}
loopbackData.loopbackProcessor->report();
@@ -661,6 +731,11 @@
delete[] loopbackData.inputShortData;
report_result:
+
+ for (int i = 0; i < loopbackData.numGlitchEvents; i++) {
+ printf(" glitch at frame %d\n", loopbackData.glitchFrames[i]);
+ }
+
written = loopbackData.loopbackProcessor->save(FILENAME_PROCESSED);
if (written > 0) {
printf("main() wrote %8d processed samples to \"%s\" on Android device\n",
diff --git a/media/libaaudio/examples/utils/AAudioArgsParser.h b/media/libaaudio/examples/utils/AAudioArgsParser.h
index a5dc55f..f5ed7aa 100644
--- a/media/libaaudio/examples/utils/AAudioArgsParser.h
+++ b/media/libaaudio/examples/utils/AAudioArgsParser.h
@@ -130,12 +130,10 @@
}
int32_t getBufferCapacity() const {
- printf("%s() returns %d\n", __func__, mBufferCapacity);
return mBufferCapacity;
}
void setBufferCapacity(int32_t frames) {
- printf("%s(%d)\n", __func__, frames);
mBufferCapacity = frames;
}
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index 72a23e3..8afb1cc 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -355,7 +355,10 @@
}
// create the IAudioRecord
- status = createRecord_l(0 /*epoch*/, mOpPackageName);
+ {
+ AutoMutex lock(mLock);
+ status = createRecord_l(0 /*epoch*/, mOpPackageName);
+ }
ALOGV("%s(%d): status %d", __func__, mPortId, status);
@@ -1358,12 +1361,14 @@
ALOGW("%s(%d): removing NULL callback!", __func__, mPortId);
return BAD_VALUE;
}
- AutoMutex lock(mLock);
- if (mDeviceCallback.unsafe_get() != callback.get()) {
- ALOGW("%s(%d): removing different callback!", __func__, mPortId);
- return INVALID_OPERATION;
+ {
+ AutoMutex lock(mLock);
+ if (mDeviceCallback.unsafe_get() != callback.get()) {
+ ALOGW("%s(%d): removing different callback!", __func__, mPortId);
+ return INVALID_OPERATION;
+ }
+ mDeviceCallback.clear();
}
- mDeviceCallback.clear();
if (mInput != AUDIO_IO_HANDLE_NONE) {
AudioSystem::removeAudioDeviceCallback(this, mInput);
}
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index b83a441e..52ad5a6 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -522,8 +522,9 @@
if (ioDesc == 0 || ioDesc->mIoHandle == AUDIO_IO_HANDLE_NONE) return;
audio_port_handle_t deviceId = AUDIO_PORT_HANDLE_NONE;
- Vector < wp<AudioDeviceCallback> > callbacks;
-
+ AudioDeviceCallbacks callbacks;
+ bool deviceValidOrChanged = false;
+ Mutex::Autolock _l(mCallbacksLock);
{
Mutex::Autolock _l(mLock);
@@ -546,6 +547,13 @@
ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(ioDesc->mIoHandle);
if (ioIndex >= 0) {
callbacks = mAudioDeviceCallbacks.valueAt(ioIndex);
+ deviceValidOrChanged = true;
+ }
+ }
+ if (event == AUDIO_OUTPUT_REGISTERED || event == AUDIO_INPUT_REGISTERED) {
+ ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(ioDesc->mIoHandle);
+ if ((ioIndex >= 0) && !mAudioDeviceCallbacks.valueAt(ioIndex).notifiedOnce()) {
+ callbacks = mAudioDeviceCallbacks.valueAt(ioIndex);
}
}
}
@@ -584,6 +592,7 @@
mIoDescriptors.replaceValueFor(ioDesc->mIoHandle, ioDesc);
if (deviceId != ioDesc->getDeviceId()) {
+ deviceValidOrChanged = true;
deviceId = ioDesc->getDeviceId();
ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(ioDesc->mIoHandle);
if (ioIndex >= 0) {
@@ -600,22 +609,28 @@
} break;
}
}
- bool callbackRemoved = false;
// callbacks.size() != 0 => ioDesc->mIoHandle and deviceId are valid
- for (size_t i = 0; i < callbacks.size(); ) {
- sp<AudioDeviceCallback> callback = callbacks[i].promote();
- if (callback.get() != nullptr) {
- callback->onAudioDeviceUpdate(ioDesc->mIoHandle, deviceId);
- i++;
- } else {
- callbacks.removeAt(i);
- callbackRemoved = true;
+ if (callbacks.size() != 0) {
+ for (size_t i = 0; i < callbacks.size(); ) {
+ sp<AudioDeviceCallback> callback = callbacks[i].promote();
+ if (callback.get() != nullptr) {
+ // Call the callback only if the device actually changed, the input or output was
+ // opened or closed or the client was newly registered and the callback was never
+ // called
+ if (!callback->notifiedOnce() || deviceValidOrChanged) {
+ // Must be called without mLock held. May lead to dead lock if calling for
+ // example getRoutedDevice that updates the device and tries to acquire mLock.
+ callback->onAudioDeviceUpdate(ioDesc->mIoHandle, deviceId);
+ callback->setNotifiedOnce();
+ }
+ i++;
+ } else {
+ callbacks.removeAt(i);
+ }
}
- }
- // clean up callback list while we are here if some clients have disappeared without
- // unregistering their callback
- if (callbackRemoved) {
- Mutex::Autolock _l(mLock);
+ callbacks.setNotifiedOnce();
+ // clean up callback list while we are here if some clients have disappeared without
+ // unregistering their callback, or if cb was served for the first time since registered
mAudioDeviceCallbacks.replaceValueFor(ioDesc->mIoHandle, callbacks);
}
}
@@ -671,8 +686,8 @@
status_t AudioSystem::AudioFlingerClient::addAudioDeviceCallback(
const wp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo)
{
- Mutex::Autolock _l(mLock);
- Vector < wp<AudioDeviceCallback> > callbacks;
+ Mutex::Autolock _l(mCallbacksLock);
+ AudioDeviceCallbacks callbacks;
ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(audioIo);
if (ioIndex >= 0) {
callbacks = mAudioDeviceCallbacks.valueAt(ioIndex);
@@ -684,7 +699,7 @@
}
}
callbacks.add(callback);
-
+ callbacks.resetNotifiedOnce();
mAudioDeviceCallbacks.replaceValueFor(audioIo, callbacks);
return NO_ERROR;
}
@@ -692,12 +707,12 @@
status_t AudioSystem::AudioFlingerClient::removeAudioDeviceCallback(
const wp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo)
{
- Mutex::Autolock _l(mLock);
+ Mutex::Autolock _l(mCallbacksLock);
ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(audioIo);
if (ioIndex < 0) {
return INVALID_OPERATION;
}
- Vector < wp<AudioDeviceCallback> > callbacks = mAudioDeviceCallbacks.valueAt(ioIndex);
+ AudioDeviceCallbacks callbacks = mAudioDeviceCallbacks.valueAt(ioIndex);
size_t cbIndex;
for (cbIndex = 0; cbIndex < callbacks.size(); cbIndex++) {
@@ -872,13 +887,14 @@
const audio_config_t *config,
audio_output_flags_t flags,
audio_port_handle_t *selectedDeviceId,
- audio_port_handle_t *portId)
+ audio_port_handle_t *portId,
+ std::vector<audio_io_handle_t> *secondaryOutputs)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return NO_INIT;
return aps->getOutputForAttr(attr, output, session, stream, pid, uid,
config,
- flags, selectedDeviceId, portId);
+ flags, selectedDeviceId, portId, secondaryOutputs);
}
status_t AudioSystem::startOutput(audio_port_handle_t portId)
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 79abea0..b5a7ebe 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -621,8 +621,10 @@
}
// create the IAudioTrack
- status = createTrack_l();
-
+ {
+ AutoMutex lock(mLock);
+ status = createTrack_l();
+ }
if (status != NO_ERROR) {
if (mAudioTrackThread != 0) {
mAudioTrackThread->requestExit(); // see comment in AudioTrack.h
@@ -2957,12 +2959,14 @@
ALOGW("%s(%d): removing NULL callback!", __func__, mPortId);
return BAD_VALUE;
}
- AutoMutex lock(mLock);
- if (mDeviceCallback.unsafe_get() != callback.get()) {
- ALOGW("%s(%d): removing different callback!", __func__, mPortId);
- return INVALID_OPERATION;
+ {
+ AutoMutex lock(mLock);
+ if (mDeviceCallback.unsafe_get() != callback.get()) {
+ ALOGW("%s removing different callback!", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+ mDeviceCallback.clear();
}
- mDeviceCallback.clear();
if (mOutput != AUDIO_IO_HANDLE_NONE) {
AudioSystem::removeAudioDeviceCallback(this, mOutput);
}
diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp
index 0db56e8..d9f6e36 100644
--- a/media/libaudioclient/IAudioPolicyService.cpp
+++ b/media/libaudioclient/IAudioPolicyService.cpp
@@ -189,16 +189,17 @@
return static_cast <audio_io_handle_t> (reply.readInt32());
}
- virtual status_t getOutputForAttr(const audio_attributes_t *attr,
- audio_io_handle_t *output,
- audio_session_t session,
- audio_stream_type_t *stream,
- pid_t pid,
- uid_t uid,
- const audio_config_t *config,
- audio_output_flags_t flags,
- audio_port_handle_t *selectedDeviceId,
- audio_port_handle_t *portId)
+ status_t getOutputForAttr(const audio_attributes_t *attr,
+ audio_io_handle_t *output,
+ audio_session_t session,
+ audio_stream_type_t *stream,
+ pid_t pid,
+ uid_t uid,
+ const audio_config_t *config,
+ audio_output_flags_t flags,
+ audio_port_handle_t *selectedDeviceId,
+ audio_port_handle_t *portId,
+ std::vector<audio_io_handle_t> *secondaryOutputs) override
{
Parcel data, reply;
data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
@@ -224,6 +225,10 @@
ALOGE("getOutputForAttr NULL portId - shouldn't happen");
return BAD_VALUE;
}
+ if (secondaryOutputs == NULL) {
+ ALOGE("getOutputForAttr NULL secondaryOutputs - shouldn't happen");
+ return BAD_VALUE;
+ }
if (attr == NULL) {
data.writeInt32(0);
} else {
@@ -258,7 +263,9 @@
}
*selectedDeviceId = (audio_port_handle_t)reply.readInt32();
*portId = (audio_port_handle_t)reply.readInt32();
- return status;
+ secondaryOutputs->resize(reply.readInt32());
+ return reply.read(secondaryOutputs->data(),
+ secondaryOutputs->size() * sizeof(audio_io_handle_t));
}
virtual status_t startOutput(audio_port_handle_t portId)
@@ -1300,16 +1307,19 @@
audio_port_handle_t selectedDeviceId = data.readInt32();
audio_port_handle_t portId = (audio_port_handle_t)data.readInt32();
audio_io_handle_t output = 0;
+ std::vector<audio_io_handle_t> secondaryOutputs;
status_t status = getOutputForAttr(hasAttributes ? &attr : NULL,
&output, session, &stream, pid, uid,
&config,
- flags, &selectedDeviceId, &portId);
+ flags, &selectedDeviceId, &portId, &secondaryOutputs);
reply->writeInt32(status);
reply->writeInt32(output);
reply->writeInt32(stream);
reply->writeInt32(selectedDeviceId);
reply->writeInt32(portId);
- return NO_ERROR;
+ reply->writeInt32(secondaryOutputs.size());
+ return reply->write(secondaryOutputs.data(),
+ secondaryOutputs.size() * sizeof(audio_io_handle_t));
} break;
case START_OUTPUT: {
diff --git a/media/libaudioclient/include/media/AudioPolicy.h b/media/libaudioclient/include/media/AudioPolicy.h
index 786fb9a..bf8d627 100644
--- a/media/libaudioclient/include/media/AudioPolicy.h
+++ b/media/libaudioclient/include/media/AudioPolicy.h
@@ -49,8 +49,12 @@
#define MIX_STATE_IDLE 0
#define MIX_STATE_MIXING 1
+/** Control to which device some audio is rendered */
#define MIX_ROUTE_FLAG_RENDER 0x1
+/** Loop back some audio instead of rendering it */
#define MIX_ROUTE_FLAG_LOOP_BACK (0x1 << 1)
+/** Loop back some audio while it is rendered */
+#define MIX_ROUTE_FLAG_LOOP_BACK_AND_RENDER (MIX_ROUTE_FLAG_RENDER | MIX_ROUTE_FLAG_LOOP_BACK)
#define MIX_ROUTE_FLAG_ALL (MIX_ROUTE_FLAG_RENDER | MIX_ROUTE_FLAG_LOOP_BACK)
#define MAX_MIXES_PER_POLICY 10
@@ -119,6 +123,11 @@
#define RECORD_CONFIG_EVENT_START 1
#define RECORD_CONFIG_EVENT_STOP 0
+static inline bool is_mix_loopback_render(uint32_t routeFlags) {
+ return (routeFlags & MIX_ROUTE_FLAG_LOOP_BACK_AND_RENDER)
+ == MIX_ROUTE_FLAG_LOOP_BACK_AND_RENDER;
+}
+
}; // namespace android
#endif // ANDROID_AUDIO_POLICY_H
diff --git a/media/libaudioclient/include/media/AudioRecord.h b/media/libaudioclient/include/media/AudioRecord.h
index 1f71844..4707c4a 100644
--- a/media/libaudioclient/include/media/AudioRecord.h
+++ b/media/libaudioclient/include/media/AudioRecord.h
@@ -677,7 +677,7 @@
sp<IMemory> mCblkMemory;
audio_track_cblk_t* mCblk; // re-load after mLock.unlock()
sp<IMemory> mBufferMemory;
- audio_io_handle_t mInput; // returned by AudioSystem::getInput()
+ audio_io_handle_t mInput = AUDIO_IO_HANDLE_NONE; // from AudioSystem::getInputforAttr()
int mPreviousPriority; // before start()
SchedPolicy mPreviousSchedulingGroup;
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index 87a9919..3df49e6 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -231,7 +231,8 @@
const audio_config_t *config,
audio_output_flags_t flags,
audio_port_handle_t *selectedDeviceId,
- audio_port_handle_t *portId);
+ audio_port_handle_t *portId,
+ std::vector<audio_io_handle_t> *secondaryOutputs);
static status_t startOutput(audio_port_handle_t portId);
static status_t stopOutput(audio_port_handle_t portId);
static void releaseOutput(audio_port_handle_t portId);
@@ -398,6 +399,15 @@
virtual void onAudioDeviceUpdate(audio_io_handle_t audioIo,
audio_port_handle_t deviceId) = 0;
+ bool notifiedOnce() const { return mNotifiedOnce; }
+ void setNotifiedOnce() { mNotifiedOnce = true; }
+ private:
+ /**
+ * @brief mNotifiedOnce it forces the callback to be called at least once when
+ * registered with a VALID AudioDevice, and allows not to flood other listeners
+ * on this iohandle that already know the valid device.
+ */
+ bool mNotifiedOnce = false;
};
static status_t addAudioDeviceCallback(const wp<AudioDeviceCallback>& callback,
@@ -443,8 +453,27 @@
private:
Mutex mLock;
DefaultKeyedVector<audio_io_handle_t, sp<AudioIoDescriptor> > mIoDescriptors;
- DefaultKeyedVector<audio_io_handle_t, Vector < wp<AudioDeviceCallback> > >
- mAudioDeviceCallbacks;
+
+ class AudioDeviceCallbacks : public Vector<wp<AudioDeviceCallback>>
+ {
+ public:
+ /**
+ * @brief notifiedOnce ensures that if a client adds a callback, it must at least be
+ * called once with the device on which it will be routed to.
+ * @return true if already notified or nobody waits for a callback, false otherwise.
+ */
+ bool notifiedOnce() const { return (size() == 0) || mNotifiedOnce; }
+ void setNotifiedOnce() { mNotifiedOnce = true; }
+ void resetNotifiedOnce() { mNotifiedOnce = false; }
+ private:
+ /**
+ * @brief mNotifiedOnce it forces each callback to be called at least once when
+ * registered with a VALID AudioDevice
+ */
+ bool mNotifiedOnce = false;
+ };
+ Mutex mCallbacksLock; // prevents race on Callbacks
+ DefaultKeyedVector<audio_io_handle_t, AudioDeviceCallbacks> mAudioDeviceCallbacks;
// cached values for recording getInputBufferSize() queries
size_t mInBuffSize; // zero indicates cache is invalid
uint32_t mInSamplingRate;
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index cbb750f..12f5d71 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -1021,7 +1021,7 @@
sp<IAudioTrack> mAudioTrack;
sp<IMemory> mCblkMemory;
audio_track_cblk_t* mCblk; // re-load after mLock.unlock()
- audio_io_handle_t mOutput; // returned by AudioSystem::getOutputForAttr()
+ audio_io_handle_t mOutput = AUDIO_IO_HANDLE_NONE; // from AudioSystem::getOutputForAttr()
sp<AudioTrackThread> mAudioTrackThread;
bool mThreadCanCallJava;
diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h
index b2cda32..e89a55d 100644
--- a/media/libaudioclient/include/media/IAudioPolicyService.h
+++ b/media/libaudioclient/include/media/IAudioPolicyService.h
@@ -66,7 +66,8 @@
const audio_config_t *config,
audio_output_flags_t flags,
audio_port_handle_t *selectedDeviceId,
- audio_port_handle_t *portId) = 0;
+ audio_port_handle_t *portId,
+ std::vector<audio_io_handle_t> *secondaryOutputs) = 0;
virtual status_t startOutput(audio_port_handle_t portId) = 0;
virtual status_t stopOutput(audio_port_handle_t portId) = 0;
virtual void releaseOutput(audio_port_handle_t portId) = 0;
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index c4015fb..6259b15 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -3595,7 +3595,7 @@
}
int64_t MPEG4Writer::Track::getDurationUs() const {
- return mTrackDurationUs + getStartTimeOffsetTimeUs();
+ return mTrackDurationUs + getStartTimeOffsetTimeUs() + mOwner->getStartTimeOffsetBFramesUs();
}
int64_t MPEG4Writer::Track::getEstimatedTrackSizeBytes() const {
@@ -4059,7 +4059,7 @@
// Prepone video playback.
if (mMinCttsOffsetTicks != mMaxCttsOffsetTicks) {
int32_t mvhdTimeScale = mOwner->getTimeScale();
- uint32_t tkhdDuration = (mTrackDurationUs * mvhdTimeScale + 5E5) / 1E6;
+ uint32_t tkhdDuration = (getDurationUs() * mvhdTimeScale + 5E5) / 1E6;
int64_t mediaTime = ((kMaxCttsOffsetTimeUs - getMinCttsOffsetTimeUs())
* mTimeScale + 5E5) / 1E6;
if (tkhdDuration > 0 && mediaTime > 0) {
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index 93478e9..3d58d4b 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -77,7 +77,8 @@
return profilingNeeded;
}
-OmxInfoBuilder sOmxInfoBuilder;
+OmxInfoBuilder sOmxInfoBuilder{true /* allowSurfaceEncoders */};
+OmxInfoBuilder sOmxNoSurfaceEncoderInfoBuilder{false /* allowSurfaceEncoders */};
Mutex sCodec2InfoBuilderMutex;
std::unique_ptr<MediaCodecListBuilderBase> sCodec2InfoBuilder;
@@ -98,7 +99,11 @@
sp<PersistentSurface> surfaceTest =
StagefrightPluginLoader::GetCCodecInstance()->createInputSurface();
if (surfaceTest == nullptr) {
+ ALOGD("Allowing all OMX codecs");
builders.push_back(&sOmxInfoBuilder);
+ } else {
+ ALOGD("Allowing only non-surface-encoder OMX codecs");
+ builders.push_back(&sOmxNoSurfaceEncoderInfoBuilder);
}
builders.push_back(GetCodec2InfoBuilder());
return builders;
@@ -219,6 +224,21 @@
return info1 == nullptr
|| (info2 != nullptr && info1->getRank() < info2->getRank());
});
+
+ // remove duplicate entries
+ bool dedupe = property_get_bool("debug.stagefright.dedupe-codecs", true);
+ if (dedupe) {
+ std::set<std::string> codecsSeen;
+ for (auto it = mCodecInfos.begin(); it != mCodecInfos.end(); ) {
+ std::string codecName = (*it)->getCodecName();
+ if (codecsSeen.count(codecName) == 0) {
+ codecsSeen.emplace(codecName);
+ it++;
+ } else {
+ it = mCodecInfos.erase(it);
+ }
+ }
+ }
}
MediaCodecList::~MediaCodecList() {
@@ -268,10 +288,17 @@
}
ssize_t MediaCodecList::findCodecByName(const char *name) const {
+ Vector<AString> aliases;
for (size_t i = 0; i < mCodecInfos.size(); ++i) {
if (strcmp(mCodecInfos[i]->getCodecName(), name) == 0) {
return i;
}
+ mCodecInfos[i]->getAliases(&aliases);
+ for (const AString &alias : aliases) {
+ if (alias == name) {
+ return i;
+ }
+ }
}
return -ENOENT;
diff --git a/media/libstagefright/MediaCodecListWriter.cpp b/media/libstagefright/MediaCodecListWriter.cpp
index b32e470..c4fb199 100644
--- a/media/libstagefright/MediaCodecListWriter.cpp
+++ b/media/libstagefright/MediaCodecListWriter.cpp
@@ -37,6 +37,16 @@
new MediaCodecInfoWriter(info.get()));
}
+std::unique_ptr<MediaCodecInfoWriter>
+ MediaCodecListWriter::findMediaCodecInfo(const char *name) {
+ for (const sp<MediaCodecInfo> &info : mCodecInfos) {
+ if (!strcmp(info->getCodecName(), name)) {
+ return std::unique_ptr<MediaCodecInfoWriter>(new MediaCodecInfoWriter(info.get()));
+ }
+ }
+ return nullptr;
+}
+
void MediaCodecListWriter::writeGlobalSettings(
const sp<AMessage> &globalSettings) const {
for (const std::pair<std::string, std::string> &kv : mGlobalSettings) {
diff --git a/media/libstagefright/OmxInfoBuilder.cpp b/media/libstagefright/OmxInfoBuilder.cpp
index 382c947..8910463 100644
--- a/media/libstagefright/OmxInfoBuilder.cpp
+++ b/media/libstagefright/OmxInfoBuilder.cpp
@@ -21,8 +21,8 @@
#define OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS
#endif
+#include <android-base/properties.h>
#include <utils/Log.h>
-#include <cutils/properties.h>
#include <media/stagefright/foundation/MediaDefs.h>
#include <media/stagefright/OmxInfoBuilder.h>
@@ -53,7 +53,7 @@
namespace /* unnamed */ {
bool hasPrefix(const hidl_string& s, const char* prefix) {
- return strncmp(s.c_str(), prefix, strlen(prefix)) == 0;
+ return strncasecmp(s.c_str(), prefix, strlen(prefix)) == 0;
}
status_t queryCapabilities(
@@ -87,7 +87,8 @@
} // unnamed namespace
-OmxInfoBuilder::OmxInfoBuilder() {
+OmxInfoBuilder::OmxInfoBuilder(bool allowSurfaceEncoders)
+ : mAllowSurfaceEncoders(allowSurfaceEncoders) {
}
status_t OmxInfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) {
@@ -135,81 +136,80 @@
// Convert roles to lists of codecs
// codec name -> index into swCodecs/hwCodecs
- std::map<hidl_string, std::unique_ptr<MediaCodecInfoWriter>>
- swCodecName2Info, hwCodecName2Info;
+ std::map<hidl_string, std::unique_ptr<MediaCodecInfoWriter>> codecName2Info;
- char rank[PROPERTY_VALUE_MAX];
- uint32_t defaultRank = 0x100;
- if (property_get("debug.stagefright.omx_default_rank", rank, nullptr)) {
- defaultRank = std::strtoul(rank, nullptr, 10);
- }
+ uint32_t defaultRank =
+ ::android::base::GetUintProperty("debug.stagefright.omx_default_rank", 0x100u);
+ uint32_t defaultSwAudioRank =
+ ::android::base::GetUintProperty("debug.stagefright.omx_default_rank.sw-audio", 0x10u);
+ uint32_t defaultSwOtherRank =
+ ::android::base::GetUintProperty("debug.stagefright.omx_default_rank.sw-other", 0x210u);
+
for (const IOmxStore::RoleInfo& role : roles) {
const hidl_string& typeName = role.type;
bool isEncoder = role.isEncoder;
- bool preferPlatformNodes = role.preferPlatformNodes;
- // If preferPlatformNodes is true, hardware nodes must be added after
- // platform (software) nodes. hwCodecs is used to hold hardware nodes
- // that need to be added after software nodes for the same role.
- std::vector<const IOmxStore::NodeInfo*> hwCodecs;
- for (const IOmxStore::NodeInfo& node : role.nodes) {
+ bool isAudio = hasPrefix(role.type, "audio/");
+ bool isVideoOrImage = hasPrefix(role.type, "video/") || hasPrefix(role.type, "image/");
+
+ for (const IOmxStore::NodeInfo &node : role.nodes) {
const hidl_string& nodeName = node.name;
+
+ // currently image and video encoders use surface input
+ if (!mAllowSurfaceEncoders && isVideoOrImage && isEncoder) {
+ ALOGD("disabling %s for media type %s because we are not using OMX input surface",
+ nodeName.c_str(), role.type.c_str());
+ continue;
+ }
+
bool isSoftware = hasPrefix(nodeName, "OMX.google");
- MediaCodecInfoWriter* info;
- if (isSoftware) {
- auto c2i = swCodecName2Info.find(nodeName);
- if (c2i == swCodecName2Info.end()) {
- // Create a new MediaCodecInfo for a new node.
- c2i = swCodecName2Info.insert(std::make_pair(
- nodeName, writer->addMediaCodecInfo())).first;
- info = c2i->second.get();
- info->setName(nodeName.c_str());
- info->setOwner(node.owner.c_str());
- info->setAttributes(
- // all OMX codecs are vendor codecs (in the vendor partition), but
- // treat OMX.google codecs as non-hardware-accelerated and non-vendor
- (isEncoder ? MediaCodecInfo::kFlagIsEncoder : 0));
- info->setRank(defaultRank);
- } else {
- // The node has been seen before. Simply retrieve the
- // existing MediaCodecInfoWriter.
- info = c2i->second.get();
- }
- } else {
- auto c2i = hwCodecName2Info.find(nodeName);
- if (c2i == hwCodecName2Info.end()) {
- // Create a new MediaCodecInfo for a new node.
- if (!preferPlatformNodes) {
- c2i = hwCodecName2Info.insert(std::make_pair(
- nodeName, writer->addMediaCodecInfo())).first;
- info = c2i->second.get();
- info->setName(nodeName.c_str());
- info->setOwner(node.owner.c_str());
- typename std::underlying_type<MediaCodecInfo::Attributes>::type attrs =
- MediaCodecInfo::kFlagIsVendor;
- if (isEncoder) {
- attrs |= MediaCodecInfo::kFlagIsEncoder;
- }
- if (std::count_if(
- node.attributes.begin(), node.attributes.end(),
- [](const IOmxStore::Attribute &i) -> bool {
- return i.key == "attribute::software-codec";
- })) {
- attrs |= MediaCodecInfo::kFlagIsHardwareAccelerated;
- }
- info->setAttributes(attrs);
- info->setRank(defaultRank);
- } else {
- // If preferPlatformNodes is true, this node must be
- // added after all software nodes.
- hwCodecs.push_back(&node);
- continue;
+ uint32_t rank = isSoftware
+ ? (isAudio ? defaultSwAudioRank : defaultSwOtherRank)
+ : defaultRank;
+ // get rank from IOmxStore via attribute
+ for (const IOmxStore::Attribute& attribute : node.attributes) {
+ if (attribute.key == "rank") {
+ uint32_t oldRank = rank;
+ char dummy;
+ if (sscanf(attribute.value.c_str(), "%u%c", &rank, &dummy) != 1) {
+ rank = oldRank;
}
- } else {
- // The node has been seen before. Simply retrieve the
- // existing MediaCodecInfoWriter.
- info = c2i->second.get();
+ break;
}
}
+
+ MediaCodecInfoWriter* info;
+ auto c2i = codecName2Info.find(nodeName);
+ if (c2i == codecName2Info.end()) {
+ // Create a new MediaCodecInfo for a new node.
+ c2i = codecName2Info.insert(std::make_pair(
+ nodeName, writer->addMediaCodecInfo())).first;
+ info = c2i->second.get();
+ info->setName(nodeName.c_str());
+ info->setOwner(node.owner.c_str());
+ info->setRank(rank);
+
+ typename std::underlying_type<MediaCodecInfo::Attributes>::type attrs = 0;
+ // all OMX codecs are vendor codecs (in the vendor partition), but
+ // treat OMX.google codecs as non-hardware-accelerated and non-vendor
+ if (!isSoftware) {
+ attrs |= MediaCodecInfo::kFlagIsVendor;
+ if (std::count_if(
+ node.attributes.begin(), node.attributes.end(),
+ [](const IOmxStore::Attribute &i) -> bool {
+ return i.key == "attribute::software-codec";
+ })) {
+ attrs |= MediaCodecInfo::kFlagIsHardwareAccelerated;
+ }
+ }
+ if (isEncoder) {
+ attrs |= MediaCodecInfo::kFlagIsEncoder;
+ }
+ info->setAttributes(attrs);
+ } else {
+ // The node has been seen before. Simply retrieve the
+ // existing MediaCodecInfoWriter.
+ info = c2i->second.get();
+ }
std::unique_ptr<MediaCodecInfo::CapabilitiesWriter> caps =
info->addMediaType(typeName.c_str());
if (queryCapabilities(
@@ -219,54 +219,8 @@
info->removeMediaType(typeName.c_str());
}
}
-
- // If preferPlatformNodes is true, hardware nodes will not have been
- // added in the loop above, but rather saved in hwCodecs. They are
- // going to be added here.
- if (preferPlatformNodes) {
- for (const IOmxStore::NodeInfo *node : hwCodecs) {
- MediaCodecInfoWriter* info;
- const hidl_string& nodeName = node->name;
- auto c2i = hwCodecName2Info.find(nodeName);
- if (c2i == hwCodecName2Info.end()) {
- // Create a new MediaCodecInfo for a new node.
- c2i = hwCodecName2Info.insert(std::make_pair(
- nodeName, writer->addMediaCodecInfo())).first;
- info = c2i->second.get();
- info->setName(nodeName.c_str());
- info->setOwner(node->owner.c_str());
- typename std::underlying_type<MediaCodecInfo::Attributes>::type attrs =
- MediaCodecInfo::kFlagIsVendor;
- if (isEncoder) {
- attrs |= MediaCodecInfo::kFlagIsEncoder;
- }
- if (std::count_if(
- node->attributes.begin(), node->attributes.end(),
- [](const IOmxStore::Attribute &i) -> bool {
- return i.key == "attribute::software-codec";
- })) {
- attrs |= MediaCodecInfo::kFlagIsHardwareAccelerated;
- }
- info->setRank(defaultRank);
- } else {
- // The node has been seen before. Simply retrieve the
- // existing MediaCodecInfoWriter.
- info = c2i->second.get();
- }
- std::unique_ptr<MediaCodecInfo::CapabilitiesWriter> caps =
- info->addMediaType(typeName.c_str());
- if (queryCapabilities(
- *node, typeName.c_str(), isEncoder, caps.get()) != OK) {
- ALOGW("Fail to add media type %s to codec %s "
- "after software codecs",
- typeName.c_str(), nodeName.c_str());
- info->removeMediaType(typeName.c_str());
- }
- }
- }
}
return OK;
}
} // namespace android
-
diff --git a/media/libstagefright/data/media_codecs_google_c2_audio.xml b/media/libstagefright/data/media_codecs_google_c2_audio.xml
index 88cd08d..f664395 100644
--- a/media/libstagefright/data/media_codecs_google_c2_audio.xml
+++ b/media/libstagefright/data/media_codecs_google_c2_audio.xml
@@ -17,51 +17,61 @@
<Included>
<Decoders>
<MediaCodec name="c2.android.mp3.decoder" type="audio/mpeg">
+ <Alias name="OMX.google.mp3.decoder" />
<Limit name="channel-count" max="2" />
<Limit name="sample-rate" ranges="8000,11025,12000,16000,22050,24000,32000,44100,48000" />
<Limit name="bitrate" range="8000-320000" />
</MediaCodec>
<MediaCodec name="c2.android.amrnb.decoder" type="audio/3gpp">
+ <Alias name="OMX.google.amrnb.decoder" />
<Limit name="channel-count" max="1" />
<Limit name="sample-rate" ranges="8000" />
<Limit name="bitrate" range="4750-12200" />
</MediaCodec>
<MediaCodec name="c2.android.amrwb.decoder" type="audio/amr-wb">
+ <Alias name="OMX.google.amrwb.decoder" />
<Limit name="channel-count" max="1" />
<Limit name="sample-rate" ranges="16000" />
<Limit name="bitrate" range="6600-23850" />
</MediaCodec>
<MediaCodec name="c2.android.aac.decoder" type="audio/mp4a-latm">
+ <Alias name="OMX.google.aac.decoder" />
<Limit name="channel-count" max="8" />
<Limit name="sample-rate" ranges="7350,8000,11025,12000,16000,22050,24000,32000,44100,48000" />
<Limit name="bitrate" range="8000-960000" />
</MediaCodec>
<MediaCodec name="c2.android.g711.alaw.decoder" type="audio/g711-alaw">
+ <Alias name="OMX.google.g711.alaw.decoder" />
<Limit name="channel-count" max="1" />
<Limit name="sample-rate" ranges="8000-48000" />
<Limit name="bitrate" range="64000" />
</MediaCodec>
<MediaCodec name="c2.android.g711.mlaw.decoder" type="audio/g711-mlaw">
+ <Alias name="OMX.google.g711.mlaw.decoder" />
<Limit name="channel-count" max="1" />
<Limit name="sample-rate" ranges="8000-48000" />
<Limit name="bitrate" range="64000" />
</MediaCodec>
<MediaCodec name="c2.android.vorbis.decoder" type="audio/vorbis">
+ <Alias name="OMX.google.vorbis.decoder" />
<Limit name="channel-count" max="8" />
<Limit name="sample-rate" ranges="8000-96000" />
<Limit name="bitrate" range="32000-500000" />
</MediaCodec>
<MediaCodec name="c2.android.opus.decoder" type="audio/opus">
+ <Alias name="OMX.google.opus.decoder" />
<Limit name="channel-count" max="8" />
<Limit name="sample-rate" ranges="48000" />
<Limit name="bitrate" range="6000-510000" />
</MediaCodec>
<MediaCodec name="c2.android.raw.decoder" type="audio/raw">
+ <Alias name="OMX.google.raw.decoder" />
<Limit name="channel-count" max="8" />
<Limit name="sample-rate" ranges="8000-96000" />
<Limit name="bitrate" range="1-10000000" />
</MediaCodec>
<MediaCodec name="c2.android.flac.decoder" type="audio/flac">
+ <Alias name="OMX.google.flac.decoder" />
<Limit name="channel-count" max="8" />
<Limit name="sample-rate" ranges="1-655350" />
<Limit name="bitrate" range="1-21000000" />
@@ -69,24 +79,28 @@
</Decoders>
<Encoders>
<MediaCodec name="c2.android.aac.encoder" type="audio/mp4a-latm">
+ <Alias name="OMX.google.aac.decoder" />
<Limit name="channel-count" max="6" />
<Limit name="sample-rate" ranges="8000,11025,12000,16000,22050,24000,32000,44100,48000" />
<!-- also may support 64000, 88200 and 96000 Hz -->
<Limit name="bitrate" range="8000-960000" />
</MediaCodec>
<MediaCodec name="c2.android.amrnb.encoder" type="audio/3gpp">
+ <Alias name="OMX.google.amrnb.decoder" />
<Limit name="channel-count" max="1" />
<Limit name="sample-rate" ranges="8000" />
<Limit name="bitrate" range="4750-12200" />
<Feature name="bitrate-modes" value="CBR" />
</MediaCodec>
<MediaCodec name="c2.android.amrwb.encoder" type="audio/amr-wb">
+ <Alias name="OMX.google.amrwb.decoder" />
<Limit name="channel-count" max="1" />
<Limit name="sample-rate" ranges="16000" />
<Limit name="bitrate" range="6600-23850" />
<Feature name="bitrate-modes" value="CBR" />
</MediaCodec>
<MediaCodec name="c2.android.flac.encoder" type="audio/flac">
+ <Alias name="OMX.google.flac.decoder" />
<Limit name="channel-count" max="2" />
<Limit name="sample-rate" ranges="1-655350" />
<Limit name="bitrate" range="1-21000000" />
diff --git a/media/libstagefright/data/media_codecs_google_c2_telephony.xml b/media/libstagefright/data/media_codecs_google_c2_telephony.xml
index d1055b3..950b092 100644
--- a/media/libstagefright/data/media_codecs_google_c2_telephony.xml
+++ b/media/libstagefright/data/media_codecs_google_c2_telephony.xml
@@ -17,6 +17,7 @@
<Included>
<Decoders>
<MediaCodec name="c2.android.gsm.decoder" type="audio/gsm">
+ <Alias name="OMX.google.gsm.decoder" />
<Limit name="channel-count" max="1" />
<Limit name="sample-rate" ranges="8000" />
<Limit name="bitrate" range="13000" />
diff --git a/media/libstagefright/data/media_codecs_google_c2_tv.xml b/media/libstagefright/data/media_codecs_google_c2_tv.xml
index fa082c7..1b00dc9 100644
--- a/media/libstagefright/data/media_codecs_google_c2_tv.xml
+++ b/media/libstagefright/data/media_codecs_google_c2_tv.xml
@@ -17,6 +17,7 @@
<Included>
<Decoders>
<MediaCodec name="c2.android.mpeg2.decoder" type="video/mpeg2">
+ <Alias name="OMX.google.mpeg2.decoder" />
<!-- profiles and levels: ProfileMain : LevelHL -->
<Limit name="size" min="16x16" max="1920x1088" />
<Limit name="alignment" value="2x2" />
diff --git a/media/libstagefright/data/media_codecs_google_c2_video.xml b/media/libstagefright/data/media_codecs_google_c2_video.xml
index c49789e..5c2d96d 100644
--- a/media/libstagefright/data/media_codecs_google_c2_video.xml
+++ b/media/libstagefright/data/media_codecs_google_c2_video.xml
@@ -17,6 +17,7 @@
<Included>
<Decoders>
<MediaCodec name="c2.android.mpeg4.decoder" type="video/mp4v-es">
+ <Alias name="OMX.google.mpeg4.decoder" />
<!-- profiles and levels: ProfileSimple : Level3 -->
<Limit name="size" min="2x2" max="352x288" />
<Limit name="alignment" value="2x2" />
@@ -26,6 +27,7 @@
<Feature name="adaptive-playback" />
</MediaCodec>
<MediaCodec name="c2.android.h263.decoder" type="video/3gpp">
+ <Alias name="OMX.google.h263.decoder" />
<!-- profiles and levels: ProfileBaseline : Level30, ProfileBaseline : Level45
ProfileISWV2 : Level30, ProfileISWV2 : Level45 -->
<Limit name="size" min="2x2" max="352x288" />
@@ -34,6 +36,7 @@
<Feature name="adaptive-playback" />
</MediaCodec>
<MediaCodec name="c2.android.avc.decoder" type="video/avc">
+ <Alias name="OMX.google.h264.decoder" />
<!-- profiles and levels: ProfileHigh : Level52 -->
<Limit name="size" min="2x2" max="4080x4080" />
<Limit name="alignment" value="2x2" />
@@ -44,6 +47,7 @@
<Feature name="adaptive-playback" />
</MediaCodec>
<MediaCodec name="c2.android.hevc.decoder" type="video/hevc">
+ <Alias name="OMX.google.hevc.decoder" />
<!-- profiles and levels: ProfileMain : MainTierLevel51 -->
<Limit name="size" min="2x2" max="4096x4096" />
<Limit name="alignment" value="2x2" />
@@ -54,6 +58,7 @@
<Feature name="adaptive-playback" />
</MediaCodec>
<MediaCodec name="c2.android.vp8.decoder" type="video/x-vnd.on2.vp8">
+ <Alias name="OMX.google.vp8.decoder" />
<Limit name="size" min="2x2" max="2048x2048" />
<Limit name="alignment" value="2x2" />
<Limit name="block-size" value="16x16" />
@@ -63,6 +68,7 @@
<Feature name="adaptive-playback" />
</MediaCodec>
<MediaCodec name="c2.android.vp9.decoder" type="video/x-vnd.on2.vp9">
+ <Alias name="OMX.google.vp9.decoder" />
<Limit name="size" min="2x2" max="2048x2048" />
<Limit name="alignment" value="2x2" />
<Limit name="block-size" value="16x16" />
@@ -84,12 +90,14 @@
<Encoders>
<MediaCodec name="c2.android.h263.encoder" type="video/3gpp">
+ <Alias name="OMX.google.h263.encoder" />
<!-- profiles and levels: ProfileBaseline : Level45 -->
<Limit name="size" min="176x144" max="176x144" />
<Limit name="alignment" value="16x16" />
<Limit name="bitrate" range="1-128000" />
</MediaCodec>
<MediaCodec name="c2.android.avc.encoder" type="video/avc">
+ <Alias name="OMX.google.h264.encoder" />
<!-- profiles and levels: ProfileBaseline : Level41 -->
<Limit name="size" min="16x16" max="2048x2048" />
<Limit name="alignment" value="2x2" />
@@ -100,6 +108,7 @@
<Feature name="intra-refresh" />
</MediaCodec>
<MediaCodec name="c2.android.mpeg4.encoder" type="video/mp4v-es">
+ <Alias name="OMX.google.mpeg4.encoder" />
<!-- profiles and levels: ProfileCore : Level2 -->
<Limit name="size" min="16x16" max="176x144" />
<Limit name="alignment" value="16x16" />
@@ -108,6 +117,7 @@
<Limit name="bitrate" range="1-64000" />
</MediaCodec>
<MediaCodec name="c2.android.vp8.encoder" type="video/x-vnd.on2.vp8">
+ <Alias name="OMX.google.vp8.encoder" />
<!-- profiles and levels: ProfileMain : Level_Version0-3 -->
<Limit name="size" min="2x2" max="2048x2048" />
<Limit name="alignment" value="2x2" />
@@ -118,6 +128,7 @@
<Feature name="bitrate-modes" value="VBR,CBR" />
</MediaCodec>
<MediaCodec name="c2.android.vp9.encoder" type="video/x-vnd.on2.vp9">
+ <Alias name="OMX.google.vp9.encoder" />
<!-- profiles and levels: ProfileMain : Level_Version0-3 -->
<Limit name="size" min="2x2" max="2048x2048" />
<Limit name="alignment" value="2x2" />
diff --git a/media/libstagefright/include/media/stagefright/MediaCodecListWriter.h b/media/libstagefright/include/media/stagefright/MediaCodecListWriter.h
index 59f57c7..f53b23e 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodecListWriter.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodecListWriter.h
@@ -48,6 +48,13 @@
* added `MediaCodecInfo` object.
*/
std::unique_ptr<MediaCodecInfoWriter> addMediaCodecInfo();
+ /**
+ * Find an existing `MediaCodecInfo` object for a codec name and return a
+ * `MediaCodecInfoWriter` object associated with the found added `MediaCodecInfo`.
+ *
+ * @return The `MediaCodecInfoWriter` object if found, or nullptr if not found.
+ */
+ std::unique_ptr<MediaCodecInfoWriter> findMediaCodecInfo(const char *codecName);
private:
MediaCodecListWriter() = default;
diff --git a/media/libstagefright/include/media/stagefright/OmxInfoBuilder.h b/media/libstagefright/include/media/stagefright/OmxInfoBuilder.h
index 28f6094..1410a16 100644
--- a/media/libstagefright/include/media/stagefright/OmxInfoBuilder.h
+++ b/media/libstagefright/include/media/stagefright/OmxInfoBuilder.h
@@ -23,8 +23,11 @@
namespace android {
class OmxInfoBuilder : public MediaCodecListBuilderBase {
+private:
+ bool mAllowSurfaceEncoders; // allow surface encoders
+
public:
- OmxInfoBuilder();
+ explicit OmxInfoBuilder(bool allowSurfaceEncoders);
~OmxInfoBuilder() override = default;
status_t buildMediaCodecList(MediaCodecListWriter* writer) override;
};
diff --git a/media/libstagefright/omx/1.0/OmxStore.cpp b/media/libstagefright/omx/1.0/OmxStore.cpp
index 447af6f..2e041e3 100644
--- a/media/libstagefright/omx/1.0/OmxStore.cpp
+++ b/media/libstagefright/omx/1.0/OmxStore.cpp
@@ -61,10 +61,7 @@
role.role = rolePair.first;
role.type = rolePair.second.type;
role.isEncoder = rolePair.second.isEncoder;
- // TODO: Currently, preferPlatformNodes information is not available in
- // the xml file. Once we have a way to provide this information, it
- // should be parsed properly.
- role.preferPlatformNodes = rolePair.first.compare(0, 5, "audio") == 0;
+ role.preferPlatformNodes = false; // deprecated and ignored, using rank instead
hidl_vec<NodeInfo>& nodeList = role.nodes;
nodeList.resize(rolePair.second.nodeList.size());
size_t j = 0;
diff --git a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
index 6e541ba..7046f61 100644
--- a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
+++ b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
@@ -502,6 +502,7 @@
const char *name = nullptr;
const char *type = nullptr;
const char *update = nullptr;
+ const char *rank = nullptr;
size_t i = 0;
while (attrs[i] != nullptr) {
@@ -523,6 +524,12 @@
return BAD_VALUE;
}
update = attrs[i];
+ } else if (strEq(attrs[i], "rank")) {
+ if (attrs[++i] == nullptr) {
+ ALOGE("addMediaCodecFromAttributes: rank is null");
+ return BAD_VALUE;
+ }
+ rank = attrs[i];
} else {
ALOGE("addMediaCodecFromAttributes: unrecognized attribute: %s", attrs[i]);
return BAD_VALUE;
@@ -579,6 +586,15 @@
}
}
+ if (rank != nullptr) {
+ if (!mCurrentCodec->second.rank.empty() && mCurrentCodec->second.rank != rank) {
+ ALOGE("addMediaCodecFromAttributes: code \"%s\" rank changed from \"%s\" to \"%s\"",
+ name, mCurrentCodec->second.rank.c_str(), rank);
+ return BAD_VALUE;
+ }
+ mCurrentCodec->second.rank = rank;
+ }
+
return OK;
}
@@ -1035,6 +1051,7 @@
const auto& codecName = codec.first;
bool isEncoder = codec.second.isEncoder;
size_t order = codec.second.order;
+ std::string rank = codec.second.rank;
const auto& typeMap = codec.second.typeMap;
for (const auto& type : typeMap) {
const auto& typeName = type.first;
@@ -1090,6 +1107,9 @@
nodeInfo.attributeList.push_back(Attribute{quirk, "present"});
}
}
+ if (!rank.empty()) {
+ nodeInfo.attributeList.push_back(Attribute{"rank", rank});
+ }
nodeList->insert(std::make_pair(
std::move(order), std::move(nodeInfo)));
}
diff --git a/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h b/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h
index fd949da..7a986b7 100644
--- a/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h
+++ b/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h
@@ -66,6 +66,7 @@
QuirkSet quirkSet; ///< Set of quirks requested by this codec
TypeMap typeMap; ///< Map of types supported by this codec
std::vector<std::string> aliases; ///< Name aliases for this codec
+ std::string rank; ///< Rank of this codec. This is a numeric string.
};
typedef std::pair<std::string, CodecProperties> Codec;
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index bc99099..7733071 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -292,13 +292,16 @@
fullConfig.sample_rate = config->sample_rate;
fullConfig.channel_mask = config->channel_mask;
fullConfig.format = config->format;
+ std::vector<audio_io_handle_t> secondaryOutputs;
ret = AudioSystem::getOutputForAttr(attr, &io,
actualSessionId,
&streamType, client.clientPid, client.clientUid,
&fullConfig,
(audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ |
AUDIO_OUTPUT_FLAG_DIRECT),
- deviceId, &portId);
+ deviceId, &portId, &secondaryOutputs);
+ ALOGW_IF(!secondaryOutputs.empty(),
+ "%s does not support secondary outputs, ignoring them", __func__);
} else {
ret = AudioSystem::getInputForAttr(attr, &io,
actualSessionId,
@@ -678,6 +681,7 @@
status_t lStatus;
audio_stream_type_t streamType;
audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
+ std::vector<audio_io_handle_t> secondaryOutputs;
bool updatePid = (input.clientInfo.clientPid == -1);
const uid_t callingUid = IPCThreadState::self()->getCallingUid();
@@ -712,7 +716,7 @@
lStatus = AudioSystem::getOutputForAttr(&input.attr, &output.outputId, sessionId, &streamType,
clientPid, clientUid, &input.config, input.flags,
- &output.selectedDeviceId, &portId);
+ &output.selectedDeviceId, &portId, &secondaryOutputs);
if (lStatus != NO_ERROR || output.outputId == AUDIO_IO_HANDLE_NONE) {
ALOGE("createTrack() getOutputForAttr() return error %d or invalid output handle", lStatus);
@@ -785,6 +789,59 @@
output.afLatencyMs = thread->latency();
output.portId = portId;
+ if (lStatus == NO_ERROR) {
+ // Connect secondary outputs. Failure on a secondary output must not imped the primary
+ // Any secondary output setup failure will lead to a desync between the AP and AF until
+ // the track is destroyed.
+ TeePatches teePatches;
+ for (audio_io_handle_t secondaryOutput : secondaryOutputs) {
+ PlaybackThread *secondaryThread = checkPlaybackThread_l(secondaryOutput);
+ if (secondaryThread == NULL) {
+ ALOGE("no playback thread found for secondary output %d", output.outputId);
+ continue;
+ }
+
+ size_t frameCount = std::lcm(thread->frameCount(), secondaryThread->frameCount());
+
+ using namespace std::chrono_literals;
+ auto inChannelMask = audio_channel_mask_out_to_in(input.config.channel_mask);
+ sp patchRecord = new RecordThread::PatchRecord(nullptr /* thread */,
+ output.sampleRate,
+ inChannelMask,
+ input.config.format,
+ frameCount,
+ NULL /* buffer */,
+ (size_t)0 /* bufferSize */,
+ AUDIO_INPUT_FLAG_DIRECT,
+ 0ns /* timeout */);
+ status_t status = patchRecord->initCheck();
+ if (status != NO_ERROR) {
+ ALOGE("Secondary output patchRecord init failed: %d", status);
+ continue;
+ }
+ sp patchTrack = new PlaybackThread::PatchTrack(secondaryThread,
+ streamType,
+ output.sampleRate,
+ input.config.channel_mask,
+ input.config.format,
+ frameCount,
+ patchRecord->buffer(),
+ patchRecord->bufferSize(),
+ output.flags,
+ 0ns /* timeout */);
+ status = patchTrack->initCheck();
+ if (status != NO_ERROR) {
+ ALOGE("Secondary output patchTrack init failed: %d", status);
+ continue;
+ }
+ teePatches.push_back({patchRecord, patchTrack});
+ secondaryThread->addPatchTrack(patchTrack);
+ patchTrack->setPeerProxy(patchRecord.get());
+ patchRecord->setPeerProxy(patchTrack.get());
+ }
+ track->setTeePatches(std::move(teePatches));
+ }
+
// move effect chain to this output thread if an effect on same session was waiting
// for a track to be created
if (lStatus == NO_ERROR && effectThread != NULL) {
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index d8c0da5..1441e15 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -21,8 +21,11 @@
#include "Configuration.h"
#include <atomic>
#include <mutex>
+#include <chrono>
#include <deque>
#include <map>
+#include <numeric>
+#include <optional>
#include <set>
#include <string>
#include <vector>
@@ -526,6 +529,9 @@
class EffectChain;
struct AudioStreamIn;
+ struct TeePatch;
+ using TeePatches = std::vector<TeePatch>;
+
struct stream_type_t {
stream_type_t()
@@ -725,6 +731,11 @@
audioHwDev(dev), stream(in), flags(flags) {}
};
+ struct TeePatch {
+ sp<RecordThread::PatchRecord> patchRecord;
+ sp<PlaybackThread::PatchTrack> patchTrack;
+ };
+
// for mAudioSessionRefs only
struct AudioSessionRef {
AudioSessionRef(audio_session_t sessionid, pid_t pid) :
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index e78c98b..c5b9953 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -139,6 +139,75 @@
}
}
+void FastMixer::updateMixerTrack(int index, Reason reason) {
+ const FastMixerState * const current = (const FastMixerState *) mCurrent;
+ const FastTrack * const fastTrack = ¤t->mFastTracks[index];
+
+ // check and update generation
+ if (reason == REASON_MODIFY && mGenerations[index] == fastTrack->mGeneration) {
+ return; // no change on an already configured track.
+ }
+ mGenerations[index] = fastTrack->mGeneration;
+
+ // mMixer == nullptr on configuration failure (check done after generation update).
+ if (mMixer == nullptr) {
+ return;
+ }
+
+ switch (reason) {
+ case REASON_REMOVE:
+ mMixer->destroy(index);
+ break;
+ case REASON_ADD: {
+ const status_t status = mMixer->create(
+ index, fastTrack->mChannelMask, fastTrack->mFormat, AUDIO_SESSION_OUTPUT_MIX);
+ LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
+ "%s: cannot create fast track index"
+ " %d, mask %#x, format %#x in AudioMixer",
+ __func__, index, fastTrack->mChannelMask, fastTrack->mFormat);
+ }
+ [[fallthrough]]; // now fallthrough to update the newly created track.
+ case REASON_MODIFY:
+ mMixer->setBufferProvider(index, fastTrack->mBufferProvider);
+
+ float vlf, vrf;
+ if (fastTrack->mVolumeProvider != nullptr) {
+ const gain_minifloat_packed_t vlr = fastTrack->mVolumeProvider->getVolumeLR();
+ vlf = float_from_gain(gain_minifloat_unpack_left(vlr));
+ vrf = float_from_gain(gain_minifloat_unpack_right(vlr));
+ } else {
+ vlf = vrf = AudioMixer::UNITY_GAIN_FLOAT;
+ }
+
+ // set volume to avoid ramp whenever the track is updated (or created).
+ // Note: this does not distinguish from starting fresh or
+ // resuming from a paused state.
+ mMixer->setParameter(index, AudioMixer::VOLUME, AudioMixer::VOLUME0, &vlf);
+ mMixer->setParameter(index, AudioMixer::VOLUME, AudioMixer::VOLUME1, &vrf);
+
+ mMixer->setParameter(index, AudioMixer::RESAMPLE, AudioMixer::REMOVE, nullptr);
+ mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
+ (void *)mMixerBuffer);
+ mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::MIXER_FORMAT,
+ (void *)(uintptr_t)mMixerBufferFormat);
+ mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::FORMAT,
+ (void *)(uintptr_t)fastTrack->mFormat);
+ mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK,
+ (void *)(uintptr_t)fastTrack->mChannelMask);
+ mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK,
+ (void *)(uintptr_t)mSinkChannelMask);
+ mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::HAPTIC_ENABLED,
+ (void *)(uintptr_t)fastTrack->mHapticPlaybackEnabled);
+ mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::HAPTIC_INTENSITY,
+ (void *)(uintptr_t)fastTrack->mHapticIntensity);
+
+ mMixer->enable(index);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("%s: invalid update reason %d", __func__, reason);
+ }
+}
+
void FastMixer::onStateChange()
{
const FastMixerState * const current = (const FastMixerState *) mCurrent;
@@ -240,21 +309,16 @@
// check for change in active track set
const unsigned currentTrackMask = current->mTrackMask;
dumpState->mTrackMask = currentTrackMask;
+ dumpState->mNumTracks = popcount(currentTrackMask);
if (current->mFastTracksGen != mFastTracksGen) {
- ALOG_ASSERT(mMixerBuffer != NULL);
// process removed tracks first to avoid running out of track names
unsigned removedTracks = previousTrackMask & ~currentTrackMask;
while (removedTracks != 0) {
int i = __builtin_ctz(removedTracks);
removedTracks &= ~(1 << i);
- const FastTrack* fastTrack = ¤t->mFastTracks[i];
- ALOG_ASSERT(fastTrack->mBufferProvider == NULL);
- if (mMixer != NULL) {
- mMixer->destroy(i);
- }
+ updateMixerTrack(i, REASON_REMOVE);
// don't reset track dump state, since other side is ignoring it
- mGenerations[i] = fastTrack->mGeneration;
}
// now process added tracks
@@ -262,40 +326,7 @@
while (addedTracks != 0) {
int i = __builtin_ctz(addedTracks);
addedTracks &= ~(1 << i);
- const FastTrack* fastTrack = ¤t->mFastTracks[i];
- AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider;
- if (mMixer != NULL) {
- const int name = i; // for clarity, choose name as fast track index.
- status_t status = mMixer->create(
- name,
- fastTrack->mChannelMask,
- fastTrack->mFormat, AUDIO_SESSION_OUTPUT_MIX);
- LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
- "%s: cannot create track name"
- " %d, mask %#x, format %#x, sessionId %d in AudioMixer",
- __func__, name,
- fastTrack->mChannelMask, fastTrack->mFormat, AUDIO_SESSION_OUTPUT_MIX);
- mMixer->setBufferProvider(name, bufferProvider);
- mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
- (void *)mMixerBuffer);
- // newly allocated track names default to full scale volume
- mMixer->setParameter(
- name,
- AudioMixer::TRACK,
- AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat);
- mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT,
- (void *)(uintptr_t)fastTrack->mFormat);
- mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK,
- (void *)(uintptr_t)fastTrack->mChannelMask);
- mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK,
- (void *)(uintptr_t)mSinkChannelMask);
- mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_ENABLED,
- (void *)(uintptr_t)fastTrack->mHapticPlaybackEnabled);
- mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_INTENSITY,
- (void *)(uintptr_t)fastTrack->mHapticIntensity);
- mMixer->enable(name);
- }
- mGenerations[i] = fastTrack->mGeneration;
+ updateMixerTrack(i, REASON_ADD);
}
// finally process (potentially) modified tracks; these use the same slot
@@ -304,44 +335,10 @@
while (modifiedTracks != 0) {
int i = __builtin_ctz(modifiedTracks);
modifiedTracks &= ~(1 << i);
- const FastTrack* fastTrack = ¤t->mFastTracks[i];
- if (fastTrack->mGeneration != mGenerations[i]) {
- // this track was actually modified
- AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider;
- ALOG_ASSERT(bufferProvider != NULL);
- if (mMixer != NULL) {
- const int name = i;
- mMixer->setBufferProvider(name, bufferProvider);
- if (fastTrack->mVolumeProvider == NULL) {
- float f = AudioMixer::UNITY_GAIN_FLOAT;
- mMixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f);
- mMixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &f);
- }
- mMixer->setParameter(name, AudioMixer::RESAMPLE,
- AudioMixer::REMOVE, NULL);
- mMixer->setParameter(
- name,
- AudioMixer::TRACK,
- AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat);
- mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT,
- (void *)(uintptr_t)fastTrack->mFormat);
- mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK,
- (void *)(uintptr_t)fastTrack->mChannelMask);
- mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK,
- (void *)(uintptr_t)mSinkChannelMask);
- mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_ENABLED,
- (void *)(uintptr_t)fastTrack->mHapticPlaybackEnabled);
- mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_INTENSITY,
- (void *)(uintptr_t)fastTrack->mHapticIntensity);
- // already enabled
- }
- mGenerations[i] = fastTrack->mGeneration;
- }
+ updateMixerTrack(i, REASON_MODIFY);
}
mFastTracksGen = current->mFastTracksGen;
-
- dumpState->mNumTracks = popcount(currentTrackMask);
}
}
@@ -408,8 +405,8 @@
float vlf = float_from_gain(gain_minifloat_unpack_left(vlr));
float vrf = float_from_gain(gain_minifloat_unpack_right(vlr));
- mMixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &vlf);
- mMixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &vrf);
+ mMixer->setParameter(name, AudioMixer::RAMP_VOLUME, AudioMixer::VOLUME0, &vlf);
+ mMixer->setParameter(name, AudioMixer::RAMP_VOLUME, AudioMixer::VOLUME1, &vrf);
}
// FIXME The current implementation of framesReady() for fast tracks
// takes a tryLock, which can block
diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h
index c31d476..97ab635 100644
--- a/services/audioflinger/FastMixer.h
+++ b/services/audioflinger/FastMixer.h
@@ -59,6 +59,14 @@
virtual void onStateChange();
virtual void onWork();
+ enum Reason {
+ REASON_REMOVE,
+ REASON_ADD,
+ REASON_MODIFY,
+ };
+ // called when a fast track of index has been removed, added, or modified
+ void updateMixerTrack(int index, Reason reason);
+
// FIXME these former local variables need comments
static const FastMixerState sInitial;
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index bad3ca8..3f62bc3 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -43,9 +43,8 @@
void appendDumpHeader(String8& result);
void appendDump(String8& result, bool active);
- virtual status_t start(AudioSystem::sync_event_t event =
- AudioSystem::SYNC_EVENT_NONE,
- audio_session_t triggerSession = AUDIO_SESSION_NONE);
+ virtual status_t start(AudioSystem::sync_event_t event = AudioSystem::SYNC_EVENT_NONE,
+ audio_session_t triggerSession = AUDIO_SESSION_NONE);
virtual void stop();
void pause();
@@ -129,6 +128,8 @@
}
sp<os::ExternalVibration> getExternalVibration() const { return mExternalVibration; }
+ void setTeePatches(TeePatches teePatches);
+
protected:
// for numerous
friend class PlaybackThread;
@@ -139,8 +140,8 @@
DISALLOW_COPY_AND_ASSIGN(Track);
// AudioBufferProvider interface
- virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer);
- // releaseBuffer() not overridden
+ status_t getNextBuffer(AudioBufferProvider::Buffer* buffer) override;
+ void releaseBuffer(AudioBufferProvider::Buffer* buffer) override;
// ExtendedAudioBufferProvider interface
virtual size_t framesReady() const;
@@ -220,6 +221,12 @@
sp<os::ExternalVibration> mExternalVibration;
private:
+ void interceptBuffer(const AudioBufferProvider::Buffer& buffer);
+ template <class F>
+ void forEachTeePatchTrack(F f) {
+ for (auto& tp : mTeePatches) { f(tp.patchTrack); }
+ };
+
// The following fields are only for fast tracks, and should be in a subclass
int mFastIndex; // index within FastMixerState::mFastTracks[];
// either mFastIndex == -1 if not isFastTrack()
@@ -239,6 +246,7 @@
audio_output_flags_t mFlags;
// If the last track change was notified to the client with readAndClearHasChanged
std::atomic_flag mChangeNotified = ATOMIC_FLAG_INIT;
+ TeePatches mTeePatches;
}; // end of Track
@@ -318,7 +326,7 @@
}; // end of OutputTrack
// playback track, used by PatchPanel
-class PatchTrack : public Track, public PatchProxyBufferProvider {
+class PatchTrack : public Track, public PatchTrackBase {
public:
PatchTrack(PlaybackThread *playbackThread,
@@ -329,7 +337,8 @@
size_t frameCount,
void *buffer,
size_t bufferSize,
- audio_output_flags_t flags);
+ audio_output_flags_t flags,
+ const Timeout& timeout = {});
virtual ~PatchTrack();
virtual status_t start(AudioSystem::sync_event_t event =
@@ -345,12 +354,7 @@
const struct timespec *timeOut = NULL);
virtual void releaseBuffer(Proxy::Buffer* buffer);
- void setPeerProxy(PatchProxyBufferProvider *proxy) { mPeerProxy = proxy; }
-
private:
void restartIfDisabled();
- sp<ClientProxy> mProxy;
- PatchProxyBufferProvider* mPeerProxy;
- struct timespec mPeerTimeout;
}; // end of PatchTrack
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index 32af7d5..ab4af33 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -113,7 +113,7 @@
};
// playback track, used by PatchPanel
-class PatchRecord : virtual public RecordTrack, public PatchProxyBufferProvider {
+class PatchRecord : public RecordTrack, public PatchTrackBase {
public:
PatchRecord(RecordThread *recordThread,
@@ -123,7 +123,8 @@
size_t frameCount,
void *buffer,
size_t bufferSize,
- audio_input_flags_t flags);
+ audio_input_flags_t flags,
+ const Timeout& timeout = {});
virtual ~PatchRecord();
// AudioBufferProvider interface
@@ -134,11 +135,4 @@
virtual status_t obtainBuffer(Proxy::Buffer *buffer,
const struct timespec *timeOut = NULL);
virtual void releaseBuffer(Proxy::Buffer *buffer);
-
- void setPeerProxy(PatchProxyBufferProvider *proxy) { mPeerProxy = proxy; }
-
-private:
- sp<ClientProxy> mProxy;
- PatchProxyBufferProvider* mPeerProxy;
- struct timespec mPeerTimeout;
}; // end of PatchRecord
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 5a70864..dd1eabf 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -8464,6 +8464,7 @@
audio_output_flags_t flags =
(audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ | AUDIO_OUTPUT_FLAG_DIRECT);
audio_port_handle_t deviceId = mDeviceId;
+ std::vector<audio_io_handle_t> secondaryOutputs;
ret = AudioSystem::getOutputForAttr(&mAttr, &io,
mSessionId,
&stream,
@@ -8472,7 +8473,10 @@
&config,
flags,
&deviceId,
- &portId);
+ &portId,
+ &secondaryOutputs);
+ ALOGD_IF(!secondaryOutputs.empty(),
+ "MmapThread::start does not support secondary outputs, ignoring them");
} else {
audio_config_base_t config;
config.sample_rate = mSampleRate;
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index c94639b..0ba0ab4 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -329,3 +329,19 @@
const struct timespec *requested = NULL) = 0;
virtual void releaseBuffer(Proxy::Buffer* buffer) = 0;
};
+
+class PatchTrackBase : public PatchProxyBufferProvider
+{
+public:
+ using Timeout = std::optional<std::chrono::nanoseconds>;
+ PatchTrackBase(sp<ClientProxy> proxy, const ThreadBase& thread,
+ const Timeout& timeout);
+ void setPeerTimeout(std::chrono::nanoseconds timeout);
+ void setPeerProxy(PatchProxyBufferProvider *proxy) { mPeerProxy = proxy; }
+
+protected:
+ const sp<ClientProxy> mProxy;
+ PatchProxyBufferProvider* mPeerProxy = nullptr;
+ struct timespec mPeerTimeout{};
+
+};
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index e4af656..57dd568 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -99,7 +99,7 @@
mId(android_atomic_inc(&nextTrackId)),
mTerminated(false),
mType(type),
- mThreadIoHandle(thread->id()),
+ mThreadIoHandle(thread ? thread->id() : AUDIO_IO_HANDLE_NONE),
mPortId(portId),
mIsInvalid(false)
{
@@ -277,6 +277,27 @@
return NO_ERROR;
}
+AudioFlinger::ThreadBase::PatchTrackBase::PatchTrackBase(sp<ClientProxy> proxy,
+ const ThreadBase& thread,
+ const Timeout& timeout)
+ : mProxy(proxy)
+{
+ if (timeout) {
+ setPeerTimeout(*timeout);
+ } else {
+ // Double buffer mixer
+ uint64_t mixBufferNs = ((uint64_t)2 * thread.frameCount() * 1000000000) /
+ thread.sampleRate();
+ setPeerTimeout(std::chrono::nanoseconds{mixBufferNs});
+ }
+}
+
+void AudioFlinger::ThreadBase::PatchTrackBase::setPeerTimeout(std::chrono::nanoseconds timeout) {
+ mPeerTimeout.tv_sec = timeout.count() / std::nano::den;
+ mPeerTimeout.tv_nsec = timeout.count() % std::nano::den;
+}
+
+
// ----------------------------------------------------------------------------
// Playback
// ----------------------------------------------------------------------------
@@ -504,6 +525,7 @@
AudioSystem::releaseOutput(mPortId);
}
}
+ forEachTeePatchTrack([](auto patchTrack) { patchTrack->destroy(); });
}
void AudioFlinger::PlaybackThread::Track::appendDumpHeader(String8& result)
@@ -649,8 +671,7 @@
}
// AudioBufferProvider interface
-status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(
- AudioBufferProvider::Buffer* buffer)
+status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(AudioBufferProvider::Buffer* buffer)
{
ServerProxy::Buffer buf;
size_t desiredFrames = buffer->frameCount;
@@ -665,10 +686,39 @@
} else {
mAudioTrackServerProxy->tallyUnderrunFrames(0);
}
-
return status;
}
+void AudioFlinger::PlaybackThread::Track::releaseBuffer(AudioBufferProvider::Buffer* buffer)
+{
+ interceptBuffer(*buffer);
+ TrackBase::releaseBuffer(buffer);
+}
+
+// TODO: compensate for time shift between HW modules.
+void AudioFlinger::PlaybackThread::Track::interceptBuffer(
+ const AudioBufferProvider::Buffer& buffer) {
+ for (auto& sink : mTeePatches) {
+ RecordThread::PatchRecord& patchRecord = *sink.patchRecord;
+ AudioBufferProvider::Buffer patchBuffer;
+ patchBuffer.frameCount = buffer.frameCount;
+ auto status = patchRecord.getNextBuffer(&patchBuffer);
+ if (status != NO_ERROR) {
+ ALOGW("%s PathRecord getNextBuffer failed with error %d: %s",
+ __func__, status, strerror(-status));
+ continue;
+ }
+ // FIXME: On buffer wrap, the frame count will be less then requested,
+ // retry to write the rest. (unlikely due to lcm buffer sizing)
+ ALOGW_IF(patchBuffer.frameCount != buffer.frameCount,
+ "%s PatchRecord can not provide big enough buffer %zu/%zu, dropping %zu frames",
+ __func__, patchBuffer.frameCount, buffer.frameCount,
+ buffer.frameCount - patchBuffer.frameCount);
+ memcpy(patchBuffer.raw, buffer.raw, patchBuffer.frameCount * mFrameSize);
+ patchRecord.releaseBuffer(&patchBuffer);
+ }
+}
+
// releaseBuffer() is not overridden
// ExtendedAudioBufferProvider interface
@@ -816,6 +866,9 @@
} else {
status = BAD_VALUE;
}
+ if (status == NO_ERROR) {
+ forEachTeePatchTrack([](auto patchTrack) { patchTrack->start(); });
+ }
return status;
}
@@ -849,6 +902,7 @@
__func__, mId, (int)mThreadIoHandle);
}
}
+ forEachTeePatchTrack([](auto patchTrack) { patchTrack->stop(); });
}
void AudioFlinger::PlaybackThread::Track::pause()
@@ -881,6 +935,8 @@
break;
}
}
+ // Pausing the TeePatch to avoid a glitch on underrun, at the cost of buffered audio loss.
+ forEachTeePatchTrack([](auto patchTrack) { patchTrack->pause(); });
}
void AudioFlinger::PlaybackThread::Track::flush()
@@ -942,6 +998,8 @@
// because the hardware buffer could hold a large amount of audio
playbackThread->broadcast_l();
}
+ // Flush the Tee to avoid on resume playing old data and glitching on the transition to new data
+ forEachTeePatchTrack([](auto patchTrack) { patchTrack->flush(); });
}
// must be called with thread lock held
@@ -1060,6 +1118,11 @@
};
}
+void AudioFlinger::PlaybackThread::Track::setTeePatches(TeePatches teePatches) {
+ forEachTeePatchTrack([](auto patchTrack) { patchTrack->destroy(); });
+ mTeePatches = std::move(teePatches);
+}
+
status_t AudioFlinger::PlaybackThread::Track::getTimestamp(AudioTimestamp& timestamp)
{
if (!isOffloaded() && !isDirect()) {
@@ -1615,19 +1678,16 @@
size_t frameCount,
void *buffer,
size_t bufferSize,
- audio_output_flags_t flags)
+ audio_output_flags_t flags,
+ const Timeout& timeout)
: Track(playbackThread, NULL, streamType,
audio_attributes_t{} /* currently unused for patch track */,
sampleRate, format, channelMask, frameCount,
buffer, bufferSize, nullptr /* sharedBuffer */,
AUDIO_SESSION_NONE, AID_AUDIOSERVER, flags, TYPE_PATCH),
- mProxy(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, true, true))
+ PatchTrackBase(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, true, true),
+ *playbackThread, timeout)
{
- uint64_t mixBufferNs = ((uint64_t)2 * playbackThread->frameCount() * 1000000000) /
- playbackThread->sampleRate();
- mPeerTimeout.tv_sec = mixBufferNs / 1000000000;
- mPeerTimeout.tv_nsec = (int) (mixBufferNs % 1000000000);
-
ALOGV("%s(%d): sampleRate %d mPeerTimeout %d.%03d sec",
__func__, mId, sampleRate,
(int)mPeerTimeout.tv_sec,
@@ -2088,19 +2148,16 @@
size_t frameCount,
void *buffer,
size_t bufferSize,
- audio_input_flags_t flags)
+ audio_input_flags_t flags,
+ const Timeout& timeout)
: RecordTrack(recordThread, NULL,
audio_attributes_t{} /* currently unused for patch track */,
sampleRate, format, channelMask, frameCount,
buffer, bufferSize, AUDIO_SESSION_NONE, AID_AUDIOSERVER,
flags, TYPE_PATCH),
- mProxy(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, false, true))
+ PatchTrackBase(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, false, true),
+ *recordThread, timeout)
{
- uint64_t mixBufferNs = ((uint64_t)2 * recordThread->frameCount() * 1000000000) /
- recordThread->sampleRate();
- mPeerTimeout.tv_sec = mixBufferNs / 1000000000;
- mPeerTimeout.tv_nsec = (int) (mixBufferNs % 1000000000);
-
ALOGV("%s(%d): sampleRate %d mPeerTimeout %d.%03d sec",
__func__, mId, sampleRate,
(int)mPeerTimeout.tv_sec,
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index d7030f9..bb5441d 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -110,7 +110,8 @@
const audio_config_t *config,
audio_output_flags_t *flags,
audio_port_handle_t *selectedDeviceId,
- audio_port_handle_t *portId) = 0;
+ audio_port_handle_t *portId,
+ std::vector<audio_io_handle_t> *secondaryOutputs) = 0;
// indicates to the audio policy manager that the output starts being used by corresponding stream.
virtual status_t startOutput(audio_port_handle_t portId) = 0;
// indicates to the audio policy manager that the output stops being used by corresponding stream.
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
index 7296c95..1abce6f 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
@@ -68,13 +68,12 @@
* Try to find an output descriptor for the given attributes.
*
* @param[in] attributes to consider fowr the research of output descriptor.
- * @param[out] desc to return if an output could be found.
- *
- * @return NO_ERROR if an output was found for the given attribute (in this case, the
- * descriptor output param is initialized), error code otherwise.
+ * @param[out] desc to return if an primary output could be found.
+ * @param[out] secondaryDesc other desc that the audio should be routed to.
*/
status_t getOutputForAttr(audio_attributes_t attributes, uid_t uid,
- sp<SwAudioOutputDescriptor> &desc);
+ sp<SwAudioOutputDescriptor> &primaryDesc,
+ std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs);
sp<DeviceDescriptor> getDeviceAndMixForInputSource(audio_source_t inputSource,
const DeviceVector &availableDeviceTypes,
@@ -99,6 +98,11 @@
status_t getDevicesForUid(uid_t uid, Vector<AudioDeviceTypeAddr>& devices) const;
void dump(String8 *dst) const;
+
+private:
+ enum class MixMatchStatus { MATCH, NO_MATCH, INVALID_MIX };
+ MixMatchStatus mixMatch(const AudioMix* mix, size_t mixIndex,
+ audio_attributes_t attributes, uid_t uid);
};
} // namespace android
diff --git a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
index 4c069e4..2e44a60 100644
--- a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
@@ -86,10 +86,12 @@
audio_attributes_t attributes, audio_config_base_t config,
audio_port_handle_t preferredDeviceId, audio_stream_type_t stream,
product_strategy_t strategy, audio_output_flags_t flags,
- bool isPreferredDeviceForExclusiveUse) :
+ bool isPreferredDeviceForExclusiveUse,
+ std::vector<wp<SwAudioOutputDescriptor>> secondaryOutputs) :
ClientDescriptor(portId, uid, sessionId, attributes, config, preferredDeviceId,
isPreferredDeviceForExclusiveUse),
- mStream(stream), mStrategy(strategy), mFlags(flags) {}
+ mStream(stream), mStrategy(strategy), mFlags(flags),
+ mSecondaryOutputs(std::move(secondaryOutputs)) {}
~TrackClientDescriptor() override = default;
using ClientDescriptor::dump;
@@ -99,11 +101,15 @@
audio_output_flags_t flags() const { return mFlags; }
audio_stream_type_t stream() const { return mStream; }
product_strategy_t strategy() const { return mStrategy; }
+ const std::vector<wp<SwAudioOutputDescriptor>>& getSecondaryOutputs() const {
+ return mSecondaryOutputs;
+ };
private:
const audio_stream_type_t mStream;
const product_strategy_t mStrategy;
const audio_output_flags_t mFlags;
+ const std::vector<wp<SwAudioOutputDescriptor>> mSecondaryOutputs;
};
class RecordClientDescriptor: public ClientDescriptor
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
index 2489e76..6b6d9d2 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
@@ -156,128 +156,163 @@
}
}
-status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attributes, uid_t uid,
- sp<SwAudioOutputDescriptor> &desc)
+status_t AudioPolicyMixCollection::getOutputForAttr(
+ audio_attributes_t attributes, uid_t uid, sp<SwAudioOutputDescriptor> &primaryDesc,
+ std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs)
{
ALOGV("getOutputForAttr() querying %zu mixes:", size());
- desc = 0;
+ primaryDesc = 0;
for (size_t i = 0; i < size(); i++) {
sp<AudioPolicyMix> policyMix = valueAt(i);
+ sp<SwAudioOutputDescriptor> policyDesc = policyMix->getOutput();
+ if (!policyDesc) {
+ ALOGV("%s: Skiping %zu: Mix has no output", __func__, i);
+ continue;
+ }
+
AudioMix *mix = policyMix->getMix();
+ const bool primaryOutputMix = !is_mix_loopback_render(mix->mRouteFlags);
- if (mix->mMixType == MIX_TYPE_PLAYERS) {
- // TODO if adding more player rules (currently only 2), make rule handling "generic"
- // as there is no difference in the treatment of usage- or uid-based rules
- bool hasUsageMatchRules = false;
- bool hasUsageExcludeRules = false;
- bool usageMatchFound = false;
- bool usageExclusionFound = false;
+ if (primaryOutputMix && primaryDesc != 0) {
+ ALOGV("%s: Skiping %zu: Primary output already found", __func__, i);
+ continue; // Primary output already found
+ }
- bool hasUidMatchRules = false;
- bool hasUidExcludeRules = false;
- bool uidMatchFound = false;
- bool uidExclusionFound = false;
+ switch (mixMatch(mix, i, attributes, uid)) {
+ case MixMatchStatus::INVALID_MIX: return BAD_VALUE; // TODO: Do we really want to abort?
+ case MixMatchStatus::NO_MATCH:
+ ALOGV("%s: Mix %zu: does not match", __func__, i);
+ continue; // skip the mix
+ case MixMatchStatus::MATCH:;
+ }
- bool hasAddrMatch = false;
-
- // iterate over all mix criteria to list what rules this mix contains
- for (size_t j = 0; j < mix->mCriteria.size(); j++) {
- ALOGV(" getOutputForAttr: mix %zu: inspecting mix criteria %zu of %zu",
- i, j, mix->mCriteria.size());
-
- // if there is an address match, prioritize that match
- if (strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
- strncmp(attributes.tags + strlen("addr="),
- mix->mDeviceAddress.string(),
- AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) {
- hasAddrMatch = true;
- break;
- }
-
- switch (mix->mCriteria[j].mRule) {
- case RULE_MATCH_ATTRIBUTE_USAGE:
- ALOGV("\tmix has RULE_MATCH_ATTRIBUTE_USAGE for usage %d",
- mix->mCriteria[j].mValue.mUsage);
- hasUsageMatchRules = true;
- if (mix->mCriteria[j].mValue.mUsage == attributes.usage) {
- // found one match against all allowed usages
- usageMatchFound = true;
- }
- break;
- case RULE_EXCLUDE_ATTRIBUTE_USAGE:
- ALOGV("\tmix has RULE_EXCLUDE_ATTRIBUTE_USAGE for usage %d",
- mix->mCriteria[j].mValue.mUsage);
- hasUsageExcludeRules = true;
- if (mix->mCriteria[j].mValue.mUsage == attributes.usage) {
- // found this usage is to be excluded
- usageExclusionFound = true;
- }
- break;
- case RULE_MATCH_UID:
- ALOGV("\tmix has RULE_MATCH_UID for uid %d", mix->mCriteria[j].mValue.mUid);
- hasUidMatchRules = true;
- if (mix->mCriteria[j].mValue.mUid == uid) {
- // found one UID match against all allowed UIDs
- uidMatchFound = true;
- }
- break;
- case RULE_EXCLUDE_UID:
- ALOGV("\tmix has RULE_EXCLUDE_UID for uid %d", mix->mCriteria[j].mValue.mUid);
- hasUidExcludeRules = true;
- if (mix->mCriteria[j].mValue.mUid == uid) {
- // found this UID is to be excluded
- uidExclusionFound = true;
- }
- break;
- default:
- break;
- }
-
- // consistency checks: for each "dimension" of rules (usage, uid...), we can
- // only have MATCH rules, or EXCLUDE rules in each dimension, not a combination
- if (hasUsageMatchRules && hasUsageExcludeRules) {
- ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_ATTRIBUTE_USAGE"
- " and RULE_EXCLUDE_ATTRIBUTE_USAGE in mix %zu", i);
- return BAD_VALUE;
- }
- if (hasUidMatchRules && hasUidExcludeRules) {
- ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_UID"
- " and RULE_EXCLUDE_UID in mix %zu", i);
- return BAD_VALUE;
- }
-
- if ((hasUsageExcludeRules && usageExclusionFound)
- || (hasUidExcludeRules && uidExclusionFound)) {
- break; // stop iterating on criteria because an exclusion was found (will fail)
- }
-
- }//iterate on mix criteria
-
- // determine if exiting on success (or implicit failure as desc is 0)
- if (hasAddrMatch ||
- !((hasUsageExcludeRules && usageExclusionFound) ||
- (hasUsageMatchRules && !usageMatchFound) ||
- (hasUidExcludeRules && uidExclusionFound) ||
- (hasUidMatchRules && !uidMatchFound))) {
- ALOGV("\tgetOutputForAttr will use mix %zu", i);
- desc = policyMix->getOutput();
+ policyDesc->mPolicyMix = mix;
+ if (primaryOutputMix) {
+ primaryDesc = policyDesc;
+ ALOGV("%s: Mix %zu: set primary desc", __func__, i);
+ } else {
+ if (policyDesc->mIoHandle == AUDIO_IO_HANDLE_NONE) {
+ ALOGV("%s: Mix %zu ignored as secondaryOutput because not opened yet", __func__, i);
+ } else {
+ ALOGV("%s: Add a secondary desc %zu", __func__, i);
+ secondaryDescs->push_back(policyDesc);
}
+ }
+ }
+ return (primaryDesc == nullptr && secondaryDescs->empty()) ? BAD_VALUE : NO_ERROR;
+}
- } else if (mix->mMixType == MIX_TYPE_RECORDERS) {
- if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE &&
- strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
+AudioPolicyMixCollection::MixMatchStatus AudioPolicyMixCollection::mixMatch(
+ const AudioMix* mix, size_t mixIndex, audio_attributes_t attributes, uid_t uid) {
+
+ if (mix->mMixType == MIX_TYPE_PLAYERS) {
+ // TODO if adding more player rules (currently only 2), make rule handling "generic"
+ // as there is no difference in the treatment of usage- or uid-based rules
+ bool hasUsageMatchRules = false;
+ bool hasUsageExcludeRules = false;
+ bool usageMatchFound = false;
+ bool usageExclusionFound = false;
+
+ bool hasUidMatchRules = false;
+ bool hasUidExcludeRules = false;
+ bool uidMatchFound = false;
+ bool uidExclusionFound = false;
+
+ bool hasAddrMatch = false;
+
+ // iterate over all mix criteria to list what rules this mix contains
+ for (size_t j = 0; j < mix->mCriteria.size(); j++) {
+ ALOGV(" getOutputForAttr: mix %zu: inspecting mix criteria %zu of %zu",
+ mixIndex, j, mix->mCriteria.size());
+
+ // if there is an address match, prioritize that match
+ if (strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
strncmp(attributes.tags + strlen("addr="),
mix->mDeviceAddress.string(),
AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) {
- desc = policyMix->getOutput();
+ hasAddrMatch = true;
+ break;
}
+
+ switch (mix->mCriteria[j].mRule) {
+ case RULE_MATCH_ATTRIBUTE_USAGE:
+ ALOGV("\tmix has RULE_MATCH_ATTRIBUTE_USAGE for usage %d",
+ mix->mCriteria[j].mValue.mUsage);
+ hasUsageMatchRules = true;
+ if (mix->mCriteria[j].mValue.mUsage == attributes.usage) {
+ // found one match against all allowed usages
+ usageMatchFound = true;
+ }
+ break;
+ case RULE_EXCLUDE_ATTRIBUTE_USAGE:
+ ALOGV("\tmix has RULE_EXCLUDE_ATTRIBUTE_USAGE for usage %d",
+ mix->mCriteria[j].mValue.mUsage);
+ hasUsageExcludeRules = true;
+ if (mix->mCriteria[j].mValue.mUsage == attributes.usage) {
+ // found this usage is to be excluded
+ usageExclusionFound = true;
+ }
+ break;
+ case RULE_MATCH_UID:
+ ALOGV("\tmix has RULE_MATCH_UID for uid %d", mix->mCriteria[j].mValue.mUid);
+ hasUidMatchRules = true;
+ if (mix->mCriteria[j].mValue.mUid == uid) {
+ // found one UID match against all allowed UIDs
+ uidMatchFound = true;
+ }
+ break;
+ case RULE_EXCLUDE_UID:
+ ALOGV("\tmix has RULE_EXCLUDE_UID for uid %d", mix->mCriteria[j].mValue.mUid);
+ hasUidExcludeRules = true;
+ if (mix->mCriteria[j].mValue.mUid == uid) {
+ // found this UID is to be excluded
+ uidExclusionFound = true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ // consistency checks: for each "dimension" of rules (usage, uid...), we can
+ // only have MATCH rules, or EXCLUDE rules in each dimension, not a combination
+ if (hasUsageMatchRules && hasUsageExcludeRules) {
+ ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_ATTRIBUTE_USAGE"
+ " and RULE_EXCLUDE_ATTRIBUTE_USAGE in mix %zu", mixIndex);
+ return MixMatchStatus::INVALID_MIX;
+ }
+ if (hasUidMatchRules && hasUidExcludeRules) {
+ ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_UID"
+ " and RULE_EXCLUDE_UID in mix %zu", mixIndex);
+ return MixMatchStatus::INVALID_MIX;
+ }
+
+ if ((hasUsageExcludeRules && usageExclusionFound)
+ || (hasUidExcludeRules && uidExclusionFound)) {
+ break; // stop iterating on criteria because an exclusion was found (will fail)
+ }
+
+ }//iterate on mix criteria
+
+ // determine if exiting on success (or implicit failure as desc is 0)
+ if (hasAddrMatch ||
+ !((hasUsageExcludeRules && usageExclusionFound) ||
+ (hasUsageMatchRules && !usageMatchFound) ||
+ (hasUidExcludeRules && uidExclusionFound) ||
+ (hasUidMatchRules && !uidMatchFound))) {
+ ALOGV("\tgetOutputForAttr will use mix %zu", mixIndex);
+ return MixMatchStatus::MATCH;
}
- if (desc != 0) {
- desc->mPolicyMix = mix;
- return NO_ERROR;
+
+ } else if (mix->mMixType == MIX_TYPE_RECORDERS) {
+ if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE &&
+ strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
+ strncmp(attributes.tags + strlen("addr="),
+ mix->mDeviceAddress.string(),
+ AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) {
+ return MixMatchStatus::MATCH;
}
}
- return BAD_VALUE;
+ return MixMatchStatus::NO_MATCH;
}
sp<DeviceDescriptor> AudioPolicyMixCollection::getDeviceAndMixForOutput(
diff --git a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
index a6f6c3b..633c40e 100644
--- a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
@@ -85,7 +85,8 @@
product_strategy_t strategy) :
TrackClientDescriptor::TrackClientDescriptor(portId, uid, AUDIO_SESSION_NONE, attributes,
AUDIO_CONFIG_BASE_INITIALIZER, AUDIO_PORT_HANDLE_NONE,
- stream, strategy, AUDIO_OUTPUT_FLAG_NONE, false),
+ stream, strategy, AUDIO_OUTPUT_FLAG_NONE, false,
+ {} /* Sources do not support secondary outputs*/),
mPatchDesc(patchDesc), mSrcDevice(srcDevice)
{
}
diff --git a/services/audiopolicy/common/managerdefinitions/src/TypeConverter.cpp b/services/audiopolicy/common/managerdefinitions/src/TypeConverter.cpp
index 6f48eae..7c76d8a 100644
--- a/services/audiopolicy/common/managerdefinitions/src/TypeConverter.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/TypeConverter.cpp
@@ -45,6 +45,7 @@
const RouteFlagTypeConverter::Table RouteFlagTypeConverter::mTable[] = {
MAKE_STRING_FROM_ENUM(MIX_ROUTE_FLAG_RENDER),
MAKE_STRING_FROM_ENUM(MIX_ROUTE_FLAG_LOOP_BACK),
+ MAKE_STRING_FROM_ENUM(MIX_ROUTE_FLAG_LOOP_BACK_AND_RENDER),
MAKE_STRING_FROM_ENUM(MIX_ROUTE_FLAG_ALL),
TERMINATOR
};
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 0eee3f2..ccec93f 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -33,9 +33,12 @@
#define AUDIO_POLICY_XML_CONFIG_FILE_NAME "audio_policy_configuration.xml"
#define AUDIO_POLICY_A2DP_OFFLOAD_DISABLED_XML_CONFIG_FILE_NAME \
"audio_policy_configuration_a2dp_offload_disabled.xml"
+#define AUDIO_POLICY_BLUETOOTH_HAL_ENABLED_XML_CONFIG_FILE_NAME \
+ "audio_policy_configuration_bluetooth_hal_enabled.xml"
#include <inttypes.h>
#include <math.h>
+#include <set>
#include <unordered_set>
#include <vector>
@@ -907,16 +910,18 @@
return NO_ERROR;
}
-status_t AudioPolicyManager::getOutputForAttrInt(audio_attributes_t *resultAttr,
- audio_io_handle_t *output,
- audio_session_t session,
- const audio_attributes_t *attr,
- audio_stream_type_t *stream,
- uid_t uid,
- const audio_config_t *config,
- audio_output_flags_t *flags,
- audio_port_handle_t *selectedDeviceId,
- bool *isRequestedDeviceForExclusiveUse)
+status_t AudioPolicyManager::getOutputForAttrInt(
+ audio_attributes_t *resultAttr,
+ audio_io_handle_t *output,
+ audio_session_t session,
+ const audio_attributes_t *attr,
+ audio_stream_type_t *stream,
+ uid_t uid,
+ const audio_config_t *config,
+ audio_output_flags_t *flags,
+ audio_port_handle_t *selectedDeviceId,
+ bool *isRequestedDeviceForExclusiveUse,
+ std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs)
{
DeviceVector outputDevices;
const audio_port_handle_t requestedPortId = *selectedDeviceId;
@@ -935,19 +940,26 @@
ALOGV("%s() attributes=%s stream=%s session %d selectedDeviceId %d", __func__,
toString(*resultAttr).c_str(), toString(*stream).c_str(), session, requestedPortId);
- // 1/ First check for explicit routing (eg. setPreferredDevice): NOTE: now handled by engine
- // 2/ If no explict route, is there a matching dynamic policy that applies?
- // NOTE: new engine product strategy does not make use of dynamic routing, keep it for
- // remote-submix and legacy
- sp<SwAudioOutputDescriptor> desc;
- if (requestedDevice == nullptr &&
- mPolicyMixes.getOutputForAttr(*resultAttr, uid, desc) == NO_ERROR) {
- ALOG_ASSERT(desc != 0, "Invalid desc returned by getOutputForAttr");
- if (!audio_has_proportional_frames(config->format)) {
- return BAD_VALUE;
- }
- *output = desc->mIoHandle;
- AudioMix *mix = desc->mPolicyMix;
+ // The primary output is the explicit routing (eg. setPreferredDevice) if specified,
+ // otherwise, fallback to the dynamic policies, if none match, query the engine.
+ // Secondary outputs are always found by dynamic policies as the engine do not support them
+ sp<SwAudioOutputDescriptor> policyDesc;
+ if (mPolicyMixes.getOutputForAttr(*resultAttr, uid, policyDesc, secondaryDescs) != NO_ERROR) {
+ policyDesc = nullptr; // reset getOutputForAttr in case of failure
+ secondaryDescs->clear();
+ }
+ // Explicit routing is higher priority then any dynamic policy primary output
+ bool usePrimaryOutputFromPolicyMixes = requestedDevice == nullptr && policyDesc != nullptr;
+
+ // FIXME: in case of RENDER policy, the output capabilities should be checked
+ if ((usePrimaryOutputFromPolicyMixes || !secondaryDescs->empty())
+ && !audio_has_proportional_frames(config->format)) {
+ ALOGW("%s: audio loopback only supports proportional frames", __func__);
+ return BAD_VALUE;
+ }
+ if (usePrimaryOutputFromPolicyMixes) {
+ *output = policyDesc->mIoHandle;
+ AudioMix *mix = policyDesc->mPolicyMix;
sp<DeviceDescriptor> deviceDesc =
mAvailableOutputDevices.getDevice(mix->mDeviceType,
mix->mDeviceAddress,
@@ -1022,7 +1034,8 @@
const audio_config_t *config,
audio_output_flags_t *flags,
audio_port_handle_t *selectedDeviceId,
- audio_port_handle_t *portId)
+ audio_port_handle_t *portId,
+ std::vector<audio_io_handle_t> *secondaryOutputs)
{
// The supplied portId must be AUDIO_PORT_HANDLE_NONE
if (*portId != AUDIO_PORT_HANDLE_NONE) {
@@ -1031,11 +1044,18 @@
const audio_port_handle_t requestedPortId = *selectedDeviceId;
audio_attributes_t resultAttr;
bool isRequestedDeviceForExclusiveUse = false;
+ std::vector<sp<SwAudioOutputDescriptor>> secondaryOutputDescs;
status_t status = getOutputForAttrInt(&resultAttr, output, session, attr, stream, uid,
- config, flags, selectedDeviceId, &isRequestedDeviceForExclusiveUse);
+ config, flags, selectedDeviceId, &isRequestedDeviceForExclusiveUse,
+ &secondaryOutputDescs);
if (status != NO_ERROR) {
return status;
}
+ std::vector<wp<SwAudioOutputDescriptor>> weakSecondaryOutputDescs;
+ for (auto& secondaryDesc : secondaryOutputDescs) {
+ secondaryOutputs->push_back(secondaryDesc->mIoHandle);
+ weakSecondaryOutputDescs.push_back(secondaryDesc);
+ }
audio_config_base_t clientConfig = {.sample_rate = config->sample_rate,
.format = config->format,
@@ -1046,7 +1066,8 @@
new TrackClientDescriptor(*portId, uid, session, resultAttr, clientConfig,
requestedPortId, *stream,
mEngine->getProductStrategyForAttributes(resultAttr),
- *flags, isRequestedDeviceForExclusiveUse);
+ *flags, isRequestedDeviceForExclusiveUse,
+ std::move(weakSecondaryOutputDescs));
sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(*output);
outputDesc->addClient(clientDesc);
@@ -1562,13 +1583,15 @@
policyMix = outputDesc->mPolicyMix;
audio_devices_t newDeviceType;
address = policyMix->mDeviceAddress.string();
- if ((policyMix->mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) {
- newDeviceType = policyMix->mDeviceType;
- } else {
+ if ((policyMix->mRouteFlags & MIX_ROUTE_FLAG_LOOP_BACK) == MIX_ROUTE_FLAG_LOOP_BACK) {
newDeviceType = AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
+ } else {
+ newDeviceType = policyMix->mDeviceType;
}
- devices.add(mAvailableOutputDevices.getDevice(newDeviceType,
- String8(address), AUDIO_FORMAT_DEFAULT));
+ sp device = mAvailableOutputDevices.getDevice(newDeviceType, String8(address),
+ AUDIO_FORMAT_DEFAULT);
+ ALOG_ASSERT(device, "%s: no device found t=%u, a=%s", __func__, newDeviceType, address);
+ devices.add(device);
}
// requiresMuteCheck is false when we can bypass mute strategy.
@@ -2609,18 +2632,24 @@
// examine each mix's route type
for (size_t i = 0; i < mixes.size(); i++) {
AudioMix mix = mixes[i];
- // we only support MIX_ROUTE_FLAG_LOOP_BACK or MIX_ROUTE_FLAG_RENDER, not the combination
- if ((mix.mRouteFlags & MIX_ROUTE_FLAG_ALL) == MIX_ROUTE_FLAG_ALL) {
+ // Only capture of playback is allowed in LOOP_BACK & RENDER mode
+ if (is_mix_loopback_render(mix.mRouteFlags) && mix.mMixType != MIX_TYPE_PLAYERS) {
+ ALOGE("Unsupported Policy Mix %zu of %zu: "
+ "Only capture of playback is allowed in LOOP_BACK & RENDER mode",
+ i, mixes.size());
res = INVALID_OPERATION;
break;
}
+ // LOOP_BACK and LOOP_BACK | RENDER have the same remote submix backend and are handled
+ // in the same way.
if ((mix.mRouteFlags & MIX_ROUTE_FLAG_LOOP_BACK) == MIX_ROUTE_FLAG_LOOP_BACK) {
- ALOGV("registerPolicyMixes() mix %zu of %zu is LOOP_BACK", i, mixes.size());
+ ALOGV("registerPolicyMixes() mix %zu of %zu is LOOP_BACK %d", i, mixes.size(),
+ mix.mRouteFlags);
if (rSubmixModule == 0) {
rSubmixModule = mHwModules.getModuleFromName(
AUDIO_HARDWARE_MODULE_ID_REMOTE_SUBMIX);
if (rSubmixModule == 0) {
- ALOGE(" Unable to find audio module for submix, aborting mix %zu registration",
+ ALOGE("Unable to find audio module for submix, aborting mix %zu registration",
i);
res = INVALID_OPERATION;
break;
@@ -2635,7 +2664,7 @@
}
if (mPolicyMixes.registerMix(address, mix, 0 /*output desc*/) != NO_ERROR) {
- ALOGE(" Error registering mix %zu for address %s", i, address.string());
+ ALOGE("Error registering mix %zu for address %s", i, address.string());
res = INVALID_OPERATION;
break;
}
@@ -2679,6 +2708,8 @@
if (desc->supportedDevices().contains(device)) {
if (mPolicyMixes.registerMix(address, mix, desc) != NO_ERROR) {
+ ALOGE("Could not register mix RENDER, dev=0x%X addr=%s", type,
+ address.string());
res = INVALID_OPERATION;
} else {
foundOutput = true;
@@ -2746,7 +2777,7 @@
rSubmixModule->removeOutputProfile(address);
rSubmixModule->removeInputProfile(address);
- } if ((mix.mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) {
+ } else if ((mix.mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) {
if (mPolicyMixes.unregisterMix(mix.mDeviceAddress) != NO_ERROR) {
res = INVALID_OPERATION;
continue;
@@ -3635,9 +3666,11 @@
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE;
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
bool isRequestedDeviceForExclusiveUse = false;
+ std::vector<sp<SwAudioOutputDescriptor>> secondaryOutputs;
getOutputForAttrInt(&resultAttr, &output, AUDIO_SESSION_NONE,
&attributes, &stream, sourceDesc->uid(), &config, &flags,
- &selectedDeviceId, &isRequestedDeviceForExclusiveUse);
+ &selectedDeviceId, &isRequestedDeviceForExclusiveUse,
+ &secondaryOutputs);
if (output == AUDIO_IO_HANDLE_NONE) {
ALOGV("%s no output for device %08x", __FUNCTION__, sinkDevices.types());
return INVALID_OPERATION;
@@ -4004,7 +4037,11 @@
if (property_get_bool("ro.bluetooth.a2dp_offload.supported", false) &&
property_get_bool("persist.bluetooth.a2dp_offload.disabled", false)) {
// A2DP offload supported but disabled: try to use special XML file
- fileNames.push_back(AUDIO_POLICY_A2DP_OFFLOAD_DISABLED_XML_CONFIG_FILE_NAME);
+ if (property_get_bool("persist.bluetooth.bluetooth_audio_hal.enabled", false)) {
+ fileNames.push_back(AUDIO_POLICY_BLUETOOTH_HAL_ENABLED_XML_CONFIG_FILE_NAME);
+ } else {
+ fileNames.push_back(AUDIO_POLICY_A2DP_OFFLOAD_DISABLED_XML_CONFIG_FILE_NAME);
+ }
}
fileNames.push_back(AUDIO_POLICY_XML_CONFIG_FILE_NAME);
@@ -4782,6 +4819,7 @@
// output is suspended before any tracks are moved to it
checkA2dpSuspend();
checkOutputForAllStrategies();
+ checkSecondaryOutputs();
if (onOutputsChecked != nullptr && onOutputsChecked()) checkA2dpSuspend();
updateDevicesAndOutputs();
if (mHwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_MSD) != 0) {
@@ -4870,6 +4908,29 @@
}
}
+void AudioPolicyManager::checkSecondaryOutputs() {
+ std::set<audio_stream_type_t> streamsToInvalidate;
+ for (size_t i = 0; i < mOutputs.size(); i++) {
+ const sp<SwAudioOutputDescriptor>& outputDescriptor = mOutputs[i];
+ for (const sp<TrackClientDescriptor>& client : outputDescriptor->getClientIterable()) {
+ // FIXME code duplicated from getOutputForAttrInt
+ sp<SwAudioOutputDescriptor> desc;
+ std::vector<sp<SwAudioOutputDescriptor>> secondaryDescs;
+ mPolicyMixes.getOutputForAttr(client->attributes(), client->uid(), desc,
+ &secondaryDescs);
+ if (!std::equal(client->getSecondaryOutputs().begin(),
+ client->getSecondaryOutputs().end(),
+ secondaryDescs.begin(), secondaryDescs.end())) {
+ streamsToInvalidate.insert(client->stream());
+ }
+ }
+ }
+ for (audio_stream_type_t stream : streamsToInvalidate) {
+ ALOGD("%s Invalidate stream %d due to secondary output change", __func__, stream);
+ mpClientInterface->invalidateStream(stream);
+ }
+}
+
void AudioPolicyManager::checkA2dpSuspend()
{
audio_io_handle_t a2dpOutput = mOutputs.getA2dpOutput();
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 73c3b56..70ad6ac 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -113,15 +113,16 @@
virtual void setSystemProperty(const char* property, const char* value);
virtual status_t initCheck();
virtual audio_io_handle_t getOutput(audio_stream_type_t stream);
- virtual status_t getOutputForAttr(const audio_attributes_t *attr,
- audio_io_handle_t *output,
- audio_session_t session,
- audio_stream_type_t *stream,
- uid_t uid,
- const audio_config_t *config,
- audio_output_flags_t *flags,
- audio_port_handle_t *selectedDeviceId,
- audio_port_handle_t *portId);
+ status_t getOutputForAttr(const audio_attributes_t *attr,
+ audio_io_handle_t *output,
+ audio_session_t session,
+ audio_stream_type_t *stream,
+ uid_t uid,
+ const audio_config_t *config,
+ audio_output_flags_t *flags,
+ audio_port_handle_t *selectedDeviceId,
+ audio_port_handle_t *portId,
+ std::vector<audio_io_handle_t> *secondaryOutputs) override;
virtual status_t startOutput(audio_port_handle_t portId);
virtual status_t stopOutput(audio_port_handle_t portId);
virtual void releaseOutput(audio_port_handle_t portId);
@@ -431,6 +432,10 @@
*/
void checkOutputForAllStrategies();
+ // Same as checkOutputForStrategy but for secondary outputs. Make sure if a secondary
+ // output condition changes, the track is properly rerouted
+ void checkSecondaryOutputs();
+
// manages A2DP output suspend/restore according to phone state and BT SCO usage
void checkA2dpSuspend();
@@ -711,7 +716,8 @@
const audio_config_t *config,
audio_output_flags_t *flags,
audio_port_handle_t *selectedDeviceId,
- bool *isRequestedDeviceForExclusiveUse);
+ bool *isRequestedDeviceForExclusiveUse,
+ std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs);
// internal method to return the output handle for the given device and format
audio_io_handle_t getOutputForDevices(
const DeviceVector &devices,
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 7768ea3..8ddf824 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -175,7 +175,8 @@
const audio_config_t *config,
audio_output_flags_t flags,
audio_port_handle_t *selectedDeviceId,
- audio_port_handle_t *portId)
+ audio_port_handle_t *portId,
+ std::vector<audio_io_handle_t> *secondaryOutputs)
{
if (mAudioPolicyManager == NULL) {
return NO_INIT;
@@ -193,7 +194,8 @@
AutoCallerClear acc;
status_t result = mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid,
config,
- &flags, selectedDeviceId, portId);
+ &flags, selectedDeviceId, portId,
+ secondaryOutputs);
// FIXME: Introduce a way to check for the the telephony device before opening the output
if ((result == NO_ERROR) &&
@@ -205,9 +207,10 @@
flags = originalFlags;
*selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
*portId = AUDIO_PORT_HANDLE_NONE;
- result = mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid,
- config,
- &flags, selectedDeviceId, portId);
+ secondaryOutputs->clear();
+ result = mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid, config,
+ &flags, selectedDeviceId, portId,
+ secondaryOutputs);
}
if (result == NO_ERROR) {
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index ee293a7..8cd6e81 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -74,16 +74,17 @@
virtual status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config);
virtual audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage);
virtual audio_io_handle_t getOutput(audio_stream_type_t stream);
- virtual status_t getOutputForAttr(const audio_attributes_t *attr,
- audio_io_handle_t *output,
- audio_session_t session,
- audio_stream_type_t *stream,
- pid_t pid,
- uid_t uid,
- const audio_config_t *config,
- audio_output_flags_t flags,
- audio_port_handle_t *selectedDeviceId,
- audio_port_handle_t *portId);
+ status_t getOutputForAttr(const audio_attributes_t *attr,
+ audio_io_handle_t *output,
+ audio_session_t session,
+ audio_stream_type_t *stream,
+ pid_t pid,
+ uid_t uid,
+ const audio_config_t *config,
+ audio_output_flags_t flags,
+ audio_port_handle_t *selectedDeviceId,
+ audio_port_handle_t *portId,
+ std::vector<audio_io_handle_t> *secondaryOutputs) override;
virtual status_t startOutput(audio_port_handle_t portId);
virtual status_t stopOutput(audio_port_handle_t portId);
virtual void releaseOutput(audio_port_handle_t portId);
diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
index e9f4657..de5670c 100644
--- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp
+++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
@@ -214,7 +214,7 @@
*portId = AUDIO_PORT_HANDLE_NONE;
ASSERT_EQ(OK, mManager->getOutputForAttr(
&attr, &output, AUDIO_SESSION_NONE, &stream, 0 /*uid*/, &config, &flags,
- selectedDeviceId, portId));
+ selectedDeviceId, portId, {}));
ASSERT_NE(AUDIO_PORT_HANDLE_NONE, *portId);
}
diff --git a/services/mediacodec/Android.mk b/services/mediacodec/Android.mk
index 6a71d7d..f78c671 100644
--- a/services/mediacodec/Android.mk
+++ b/services/mediacodec/Android.mk
@@ -108,6 +108,9 @@
libutils \
libziparchive \
+LOCAL_HEADER_LIBRARIES := \
+ libnativeloader-dummy-headers \
+
LOCAL_MODULE := mediaswcodec
LOCAL_INIT_RC := mediaswcodec.rc
LOCAL_SANITIZE := scudo
diff --git a/services/mediacodec/MediaCodecUpdateService.cpp b/services/mediacodec/MediaCodecUpdateService.cpp
index 0e6892d..50ccbce 100644
--- a/services/mediacodec/MediaCodecUpdateService.cpp
+++ b/services/mediacodec/MediaCodecUpdateService.cpp
@@ -20,28 +20,12 @@
#include <android/dlext.h>
#include <dlfcn.h>
#include <media/CodecServiceRegistrant.h>
+#include <nativeloader/dlext_namespaces.h>
#include <utils/Log.h>
#include <utils/String8.h>
#include "MediaCodecUpdateService.h"
-// Copied from GraphicsEnv.cpp
-// TODO(b/37049319) Get this from a header once one exists
-extern "C" {
- android_namespace_t* android_create_namespace(const char* name,
- const char* ld_library_path,
- const char* default_library_path,
- uint64_t type,
- const char* permitted_when_isolated_path,
- android_namespace_t* parent);
- bool android_link_namespaces(android_namespace_t* from,
- android_namespace_t* to,
- const char* shared_libs_sonames);
- enum {
- ANDROID_NAMESPACE_TYPE_ISOLATED = 1,
- };
-}
-
namespace android {
void loadFromApex(const char *libDirPath) {