Merge "aaudio: command line argument parser"
diff --git a/drm/mediacas/plugins/mock/Android.mk b/drm/mediacas/plugins/mock/Android.mk
index a97fac6..a1d61da 100644
--- a/drm/mediacas/plugins/mock/Android.mk
+++ b/drm/mediacas/plugins/mock/Android.mk
@@ -28,6 +28,8 @@
LOCAL_SHARED_LIBRARIES := \
libutils liblog
+LOCAL_HEADER_LIBRARIES := media_plugin_headers
+
LOCAL_C_INCLUDES += \
$(TOP)/frameworks/av/include \
$(TOP)/frameworks/native/include/media \
diff --git a/media/libeffects/Android.bp b/media/libeffects/Android.bp
index ccaa2b4..0dd3f17 100644
--- a/media/libeffects/Android.bp
+++ b/media/libeffects/Android.bp
@@ -1 +1 @@
-subdirs = ["factory"]
+subdirs = ["factory", "config"]
diff --git a/media/libeffects/config/Android.bp b/media/libeffects/config/Android.bp
new file mode 100644
index 0000000..5666d14
--- /dev/null
+++ b/media/libeffects/config/Android.bp
@@ -0,0 +1,14 @@
+// Effect configuration
+cc_library_shared {
+ name: "libeffectsconfig",
+ vendor_available: true,
+
+ srcs: ["src/EffectsConfig.cpp"],
+
+ shared_libs: [
+ "liblog",
+ "libtinyxml2",
+ ],
+
+ export_include_dirs: ["include"],
+}
diff --git a/media/libeffects/config/include/media/EffectsConfig.h b/media/libeffects/config/include/media/EffectsConfig.h
new file mode 100644
index 0000000..811730c
--- /dev/null
+++ b/media/libeffects/config/include/media/EffectsConfig.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_MEDIA_EFFECTSCONFIG_H
+#define ANDROID_MEDIA_EFFECTSCONFIG_H
+
+/** @file Parses audio effects configuration file to C and C++ structure.
+ * @see audio_effects_conf_V2_0.xsd for documentation on each structure
+ */
+
+#include <system/audio_effect.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace android {
+namespace effectsConfig {
+
+/** Default path of effect configuration file. */
+constexpr char DEFAULT_PATH[] = "/vendor/etc/audio_effects.xml";
+
+/** Directories where the effect libraries will be search for. */
+constexpr const char* LD_EFFECT_LIBRARY_PATH[] =
+#ifdef __LP64__
+ {"/odm/lib64/soundfx", "/vendor/lib64/soundfx", "/system/lib64/soundfx"};
+#else
+ {"/odm/lib/soundfx", "/vendor/lib/soundfx", "/system/lib/soundfx"};
+#endif
+
+struct Library {
+ std::string name;
+ std::string path;
+};
+using Libraries = std::vector<Library>;
+
+struct EffectImpl {
+ Library* library; //< Only valid as long as the associated library vector is unmodified
+ effect_uuid_t uuid;
+};
+
+struct Effect : public EffectImpl {
+ std::string name;
+ bool isProxy;
+ EffectImpl libSw; //< Only valid if isProxy
+ EffectImpl libHw; //< Only valid if isProxy
+};
+
+using Effects = std::vector<Effect>;
+
+template <class Type>
+struct Stream {
+ Type type;
+ std::vector<std::reference_wrapper<Effect>> effects;
+};
+using OutputStream = Stream<audio_stream_type_t>;
+using InputStream = Stream<audio_source_t>;
+
+/** Parsed configuration.
+ * Intended to be a transient structure only used for deserialization.
+ * Note: Everything is copied in the configuration from the xml dom.
+ * If copies needed to be avoided due to performance issue,
+ * consider keeping a private handle on the xml dom and replace all strings by dom pointers.
+ * Or even better, use SAX parsing to avoid the allocations all together.
+ */
+struct Config {
+ float version;
+ Libraries libraries;
+ Effects effects;
+ std::vector<OutputStream> postprocess;
+ std::vector<InputStream> preprocess;
+};
+
+/** Result of `parse(const char*)` */
+struct ParsingResult {
+ /** Parsed config, nullptr if the xml lib could not load the file */
+ std::unique_ptr<Config> parsedConfig;
+ size_t nbSkippedElement; //< Number of skipped invalid library, effect or processing chain
+};
+
+/** Parses the provided effect configuration.
+ * Parsing do not stop of first invalid element, but continues to the next.
+ * @see ParsingResult::nbSkippedElement
+ */
+ParsingResult parse(const char* path = DEFAULT_PATH);
+
+} // namespace effectsConfig
+} // namespace android
+#endif // ANDROID_MEDIA_EFFECTSCONFIG_H
diff --git a/media/libeffects/config/src/EffectsConfig.cpp b/media/libeffects/config/src/EffectsConfig.cpp
new file mode 100644
index 0000000..97462f8
--- /dev/null
+++ b/media/libeffects/config/src/EffectsConfig.cpp
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "EffectsConfig"
+
+#include <algorithm>
+#include <cstdint>
+#include <functional>
+#include <string>
+
+#include <tinyxml2.h>
+#include <log/log.h>
+
+#include <media/EffectsConfig.h>
+
+using namespace tinyxml2;
+
+namespace android {
+namespace effectsConfig {
+
+/** All functions except `parse(const char*)` are static. */
+namespace {
+
+/** @return all `node`s children that are elements and match the tag if provided. */
+std::vector<std::reference_wrapper<const XMLElement>> getChildren(const XMLNode& node,
+ const char* childTag = nullptr) {
+ std::vector<std::reference_wrapper<const XMLElement>> children;
+ for (auto* child = node.FirstChildElement(childTag); child != nullptr;
+ child = child->NextSiblingElement(childTag)) {
+ children.emplace_back(*child);
+ }
+ return children;
+}
+
+/** @return xml dump of the provided element.
+ * By not providing a printer, it is implicitly created in the caller context.
+ * In such case the return pointer has the same lifetime as the expression containing dump().
+ */
+const char* dump(const XMLElement& element, XMLPrinter&& printer = {}) {
+ element.Accept(&printer);
+ return printer.CStr();
+}
+
+
+bool stringToUuid(const char *str, effect_uuid_t *uuid)
+{
+ uint32_t tmp[10];
+
+ if (sscanf(str, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
+ tmp, tmp+1, tmp+2, tmp+3, tmp+4, tmp+5, tmp+6, tmp+7, tmp+8, tmp+9) < 10) {
+ return false;
+ }
+ uuid->timeLow = (uint32_t)tmp[0];
+ uuid->timeMid = (uint16_t)tmp[1];
+ uuid->timeHiAndVersion = (uint16_t)tmp[2];
+ uuid->clockSeq = (uint16_t)tmp[3];
+ uuid->node[0] = (uint8_t)tmp[4];
+ uuid->node[1] = (uint8_t)tmp[5];
+ uuid->node[2] = (uint8_t)tmp[6];
+ uuid->node[3] = (uint8_t)tmp[7];
+ uuid->node[4] = (uint8_t)tmp[8];
+ uuid->node[5] = (uint8_t)tmp[9];
+
+ return true;
+}
+
+/** Map the enum and string representation of a string type.
+ * Intended to be specialized for each enum to deserialize.
+ * The general template is disabled.
+ */
+template <class Enum>
+constexpr std::enable_if<false, Enum> STREAM_NAME_MAP;
+
+/** All output stream types which support effects.
+ * This need to be kept in sink with the xsd streamOutputType.
+ */
+template <>
+constexpr std::pair<audio_stream_type_t, const char*> STREAM_NAME_MAP<audio_stream_type_t>[] = {
+ {AUDIO_STREAM_VOICE_CALL, "voice_call"},
+ {AUDIO_STREAM_SYSTEM, "system"},
+ {AUDIO_STREAM_RING, "ring"},
+ {AUDIO_STREAM_MUSIC, "music"},
+ {AUDIO_STREAM_ALARM, "alarm"},
+ {AUDIO_STREAM_NOTIFICATION, "notification"},
+ {AUDIO_STREAM_BLUETOOTH_SCO, "bluetooth_sco"},
+ {AUDIO_STREAM_ENFORCED_AUDIBLE, "enforced_audible"},
+ {AUDIO_STREAM_DTMF, "dtmf"},
+ {AUDIO_STREAM_TTS, "tts"},
+};
+
+/** All input stream types which support effects.
+ * This need to be kept in sink with the xsd streamOutputType.
+ */
+template <>
+constexpr std::pair<audio_source_t, const char*> STREAM_NAME_MAP<audio_source_t>[] = {
+ {AUDIO_SOURCE_MIC, "mic"},
+ {AUDIO_SOURCE_VOICE_UPLINK, "voice_uplink"},
+ {AUDIO_SOURCE_VOICE_DOWNLINK, "voice_downlink"},
+ {AUDIO_SOURCE_VOICE_CALL, "voice_call"},
+ {AUDIO_SOURCE_CAMCORDER, "camcorder"},
+ {AUDIO_SOURCE_VOICE_RECOGNITION, "voice_recognition"},
+ {AUDIO_SOURCE_VOICE_COMMUNICATION, "voice_communication"},
+ {AUDIO_SOURCE_UNPROCESSED, "unprocessed"},
+};
+
+/** Find the stream type enum corresponding to the stream type name or return false */
+template <class Type>
+bool stringToStreamType(const char *streamName, Type* type)
+{
+ for (auto& streamNamePair : STREAM_NAME_MAP<Type>) {
+ if (strcmp(streamNamePair.second, streamName) == 0) {
+ *type = streamNamePair.first;
+ return true;
+ }
+ }
+ return false;
+}
+
+/** Parse a library xml note and push the result in libraries or return false on failure. */
+bool parseLibrary(const XMLElement& xmlLibrary, Libraries* libraries) {
+ const char* name = xmlLibrary.Attribute("name");
+ const char* path = xmlLibrary.Attribute("path");
+ if (name == nullptr || path == nullptr) {
+ ALOGE("library must have a name and a path: %s", dump(xmlLibrary));
+ return false;
+ }
+ libraries->push_back({name, path});
+ return true;
+}
+
+/** Find an element in a collection by its name.
+ * @return nullptr if not found, the ellements address if found.
+ */
+template <class T>
+T* findByName(const char* name, std::vector<T>& collection) {
+ auto it = find_if(begin(collection), end(collection),
+ [name] (auto& item) { return item.name == name; });
+ return it != end(collection) ? &*it : nullptr;
+}
+
+/** Parse an effect from an xml element describing it.
+ * @return true and pushes the effect in effects on success,
+ * false on failure. */
+bool parseEffect(const XMLElement& xmlEffect, Libraries& libraries, Effects* effects) {
+ Effect effect{};
+
+ const char* name = xmlEffect.Attribute("name");
+ if (name == nullptr) {
+ ALOGE("%s must have a name: %s", xmlEffect.Value(), dump(xmlEffect));
+ return false;
+ }
+ effect.name = name;
+
+ // Function to parse effect.library and effect.uuid from xml
+ auto parseImpl = [&libraries](const XMLElement& xmlImpl, EffectImpl& effect) {
+ // Retrieve library name and uuid from xml
+ const char* libraryName = xmlImpl.Attribute("library");
+ const char* uuid = xmlImpl.Attribute("uuid");
+ if (libraryName == nullptr || uuid == nullptr) {
+ ALOGE("effect must have a library name and a uuid: %s", dump(xmlImpl));
+ return false;
+ }
+
+ // Convert library name to a pointer to the previously loaded library
+ auto* library = findByName(libraryName, libraries);
+ if (library == nullptr) {
+ ALOGE("Could not find library referenced in: %s", dump(xmlImpl));
+ return false;
+ }
+ effect.library = library;
+
+ if (!stringToUuid(uuid, &effect.uuid)) {
+ ALOGE("Invalid uuid in: %s", dump(xmlImpl));
+ return false;
+ }
+ return true;
+ };
+
+ if (!parseImpl(xmlEffect, effect)) {
+ return false;
+ }
+
+ // Handle proxy effects
+ effect.isProxy = false;
+ if (std::strcmp(xmlEffect.Name(), "effectProxy") == 0) {
+ effect.isProxy = true;
+
+ // Function to parse libhw and libsw
+ auto parseProxy = [&xmlEffect, &parseImpl](const char* tag, EffectImpl& proxyLib) {
+ auto* xmlProxyLib = xmlEffect.FirstChildElement(tag);
+ if (xmlProxyLib == nullptr) {
+ ALOGE("effectProxy must contain a <%s>: %s", tag, dump(*xmlProxyLib));
+ return false;
+ }
+ return parseImpl(*xmlProxyLib, proxyLib);
+ };
+ if (!parseProxy("libhw", effect.libHw) || !parseProxy("libsw", effect.libSw)) {
+ return false;
+ }
+ }
+
+ effects->push_back(std::move(effect));
+ return true;
+}
+
+/** Parse an stream from an xml element describing it.
+ * @return true and pushes the stream in streams on success,
+ * false on failure. */
+template <class Stream>
+bool parseStream(const XMLElement& xmlStream, Effects& effects, std::vector<Stream>* streams) {
+ const char* streamType = xmlStream.Attribute("type");
+ if (streamType == nullptr) {
+ ALOGE("stream must have a type: %s", dump(xmlStream));
+ return false;
+ }
+ Stream stream;
+ if (!stringToStreamType(streamType, &stream.type)) {
+ ALOGE("Invalid stream type %s: %s", streamType, dump(xmlStream));
+ return false;
+ }
+
+ for (auto& xmlApply : getChildren(xmlStream, "apply")) {
+ const char* effectName = xmlApply.get().Attribute("effect");
+ if (effectName == nullptr) {
+ ALOGE("stream/apply must have reference an effect: %s", dump(xmlApply));
+ return false;
+ }
+ auto* effect = findByName(effectName, effects);
+ if (effect == nullptr) {
+ ALOGE("Could not find effect referenced in: %s", dump(xmlApply));
+ return false;
+ }
+ stream.effects.emplace_back(*effect);
+ }
+ streams->push_back(std::move(stream));
+ return true;
+}
+
+}; // namespace
+
+ParsingResult parse(const char* path) {
+ XMLDocument doc;
+ doc.LoadFile(path);
+ if (doc.Error()) {
+ ALOGE("Failed to parse %s: Tinyxml2 error (%d): %s %s", path,
+ doc.ErrorID(), doc.GetErrorStr1(), doc.GetErrorStr2());
+ return {nullptr, 0};
+ }
+
+ auto config = std::make_unique<Config>();
+ size_t nbSkippedElements = 0;
+ auto registerFailure = [&nbSkippedElements](bool result) {
+ nbSkippedElements += result ? 0 : 1;
+ };
+ for (auto& xmlConfig : getChildren(doc, "audio_effects_conf")) {
+
+ // Parse library
+ for (auto& xmlLibraries : getChildren(xmlConfig, "libraries")) {
+ for (auto& xmlLibrary : getChildren(xmlLibraries, "library")) {
+ registerFailure(parseLibrary(xmlLibrary, &config->libraries));
+ }
+ }
+
+ // Parse effects
+ for (auto& xmlEffects : getChildren(xmlConfig, "effects")) {
+ for (auto& xmlEffect : getChildren(xmlEffects)) {
+ registerFailure(parseEffect(xmlEffect, config->libraries, &config->effects));
+ }
+ }
+
+ // Parse pre processing chains
+ for (auto& xmlPreprocess : getChildren(xmlConfig, "preprocess")) {
+ for (auto& xmlStream : getChildren(xmlPreprocess, "stream")) {
+ registerFailure(parseStream(xmlStream, config->effects, &config->preprocess));
+ }
+ }
+
+ // Parse post processing chains
+ for (auto& xmlPostprocess : getChildren(xmlConfig, "postprocess")) {
+ for (auto& xmlStream : getChildren(xmlPostprocess, "stream")) {
+ registerFailure(parseStream(xmlStream, config->effects, &config->postprocess));
+ }
+ }
+ }
+ return {std::move(config), nbSkippedElements};
+}
+
+} // namespace effectsConfig
+} // namespace android
diff --git a/media/libeffects/factory/Android.bp b/media/libeffects/factory/Android.bp
index 16680bd..21be587 100644
--- a/media/libeffects/factory/Android.bp
+++ b/media/libeffects/factory/Android.bp
@@ -10,13 +10,19 @@
cc_library_shared {
name: "libeffects",
vendor: true,
- srcs: ["EffectsFactory.c"],
+ srcs: ["EffectsFactory.c",
+ "EffectsConfigLoader.c",
+ "EffectsFactoryState.c",
+ "EffectsXmlConfigLoader.cpp",
+ ],
shared_libs: [
"libcutils",
"liblog",
"libdl",
+ "libeffectsconfig",
],
+ cflags: ["-fvisibility=hidden"],
include_dirs: ["system/media/audio_effects/include"],
@@ -25,3 +31,24 @@
header_libs: ["libeffects_headers"],
export_header_lib_headers: ["libeffects_headers"],
}
+
+cc_binary {
+ name: "dumpEffectConfigFile",
+ vendor: true,
+ srcs: ["test/DumpConfig.cpp"],
+
+ compile_multilib: "32",
+
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+
+
+ shared_libs: [
+ "libeffectsconfig",
+ "libeffects",
+ ],
+ local_include_dirs:[".", "include"],
+}
diff --git a/media/libeffects/factory/EffectsConfigLoader.c b/media/libeffects/factory/EffectsConfigLoader.c
new file mode 100644
index 0000000..5c973b4
--- /dev/null
+++ b/media/libeffects/factory/EffectsConfigLoader.c
@@ -0,0 +1,438 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "EffectsFactoryConfigLoader"
+//#define LOG_NDEBUG 0
+
+#include <dlfcn.h>
+#include <stdlib.h>
+
+#include <cutils/config_utils.h>
+#include <cutils/misc.h>
+#include <log/log.h>
+
+#include <system/audio_effects/audio_effects_conf.h>
+
+#include "EffectsConfigLoader.h"
+#include "EffectsFactoryState.h"
+
+/////////////////////////////////////////////////
+// Local functions prototypes
+/////////////////////////////////////////////////
+
+static int loadEffectConfigFile(const char *path);
+static int loadLibraries(cnode *root);
+static int loadLibrary(cnode *root, const char *name);
+static int loadEffects(cnode *root);
+static int loadEffect(cnode *node);
+// To get and add the effect pointed by the passed node to the gSubEffectList
+static int addSubEffect(cnode *root);
+static lib_entry_t *getLibrary(const char *path);
+
+static lib_entry_t *gCachedLibrary; // last library accessed by getLibrary()
+
+int EffectLoadEffectConfig()
+{
+ if (access(AUDIO_EFFECT_VENDOR_CONFIG_FILE, R_OK) == 0) {
+ return loadEffectConfigFile(AUDIO_EFFECT_VENDOR_CONFIG_FILE);
+ } else if (access(AUDIO_EFFECT_DEFAULT_CONFIG_FILE, R_OK) == 0) {
+ return loadEffectConfigFile(AUDIO_EFFECT_DEFAULT_CONFIG_FILE);
+ }
+ return 0;
+}
+
+int loadEffectConfigFile(const char *path)
+{
+ cnode *root;
+ char *data;
+
+ data = load_file(path, NULL);
+ if (data == NULL) {
+ return -ENODEV;
+ }
+ root = config_node("", "");
+ config_load(root, data);
+ loadLibraries(root);
+ loadEffects(root);
+ config_free(root);
+ free(root);
+ free(data);
+
+ return 0;
+}
+
+int loadLibraries(cnode *root)
+{
+ cnode *node;
+
+ node = config_find(root, LIBRARIES_TAG);
+ if (node == NULL) {
+ return -ENOENT;
+ }
+ node = node->first_child;
+ while (node) {
+ loadLibrary(node, node->name);
+ node = node->next;
+ }
+ return 0;
+}
+
+#ifdef __LP64__
+// audio_effects.conf always specifies 32 bit lib path: convert to 64 bit path if needed
+static const char *kLibraryPathRoot[] =
+ {"/odm/lib64/soundfx", "/vendor/lib64/soundfx", "/system/lib64/soundfx"};
+#else
+static const char *kLibraryPathRoot[] =
+ {"/odm/lib/soundfx", "/vendor/lib/soundfx", "/system/lib/soundfx"};
+#endif
+
+static const int kLibraryPathRootSize =
+ (sizeof(kLibraryPathRoot) / sizeof(kLibraryPathRoot[0]));
+
+// Checks if the library path passed as lib_path_in can be opened and if not
+// tries in standard effect library directories with just the library name and returns correct path
+// in lib_path_out
+int checkLibraryPath(const char *lib_path_in, char *lib_path_out) {
+ char *str;
+ const char *lib_name;
+ size_t len;
+
+ if (lib_path_in == NULL || lib_path_out == NULL) {
+ return -EINVAL;
+ }
+
+ strlcpy(lib_path_out, lib_path_in, PATH_MAX);
+
+ // Try exact path first
+ str = strstr(lib_path_out, "/lib/soundfx/");
+ if (str == NULL) {
+ return -EINVAL;
+ }
+
+ // Extract library name from input path
+ len = str - lib_path_out;
+ lib_name = lib_path_in + len + strlen("/lib/soundfx/");
+
+ // Then try with library name and standard path names in order of preference
+ for (int i = 0; i < kLibraryPathRootSize; i++) {
+ char path[PATH_MAX];
+
+ snprintf(path,
+ PATH_MAX,
+ "%s/%s",
+ kLibraryPathRoot[i],
+ lib_name);
+ if (F_OK == access(path, 0)) {
+ strcpy(lib_path_out, path);
+ ALOGW_IF(strncmp(lib_path_out, lib_path_in, PATH_MAX) != 0,
+ "checkLibraryPath() corrected library path %s to %s", lib_path_in, lib_path_out);
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+
+
+int loadLibrary(cnode *root, const char *name)
+{
+ cnode *node;
+ void *hdl = NULL;
+ audio_effect_library_t *desc;
+ list_elem_t *e;
+ lib_entry_t *l;
+ char path[PATH_MAX];
+
+ node = config_find(root, PATH_TAG);
+ if (node == NULL) {
+ return -EINVAL;
+ }
+
+ if (checkLibraryPath((const char *)node->value, path) != 0) {
+ ALOGW("loadLibrary() could not find library %s", path);
+ goto error;
+ }
+
+ hdl = dlopen(path, RTLD_NOW);
+ if (hdl == NULL) {
+ ALOGW("loadLibrary() failed to open %s", path);
+ goto error;
+ }
+
+ desc = (audio_effect_library_t *)dlsym(hdl, AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR);
+ if (desc == NULL) {
+ ALOGW("loadLibrary() could not find symbol %s", AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR);
+ goto error;
+ }
+
+ if (AUDIO_EFFECT_LIBRARY_TAG != desc->tag) {
+ ALOGW("getLibrary() bad tag %08x in lib info struct", desc->tag);
+ goto error;
+ }
+
+ if (EFFECT_API_VERSION_MAJOR(desc->version) !=
+ EFFECT_API_VERSION_MAJOR(EFFECT_LIBRARY_API_VERSION)) {
+ ALOGW("loadLibrary() bad lib version %08x", desc->version);
+ goto error;
+ }
+
+ // add entry for library in gLibraryList
+ l = malloc(sizeof(lib_entry_t));
+ l->name = strndup(name, PATH_MAX);
+ l->path = strndup(path, PATH_MAX);
+ l->handle = hdl;
+ l->desc = desc;
+ l->effects = NULL;
+ pthread_mutex_init(&l->lock, NULL);
+
+ e = malloc(sizeof(list_elem_t));
+ e->object = l;
+ pthread_mutex_lock(&gLibLock);
+ e->next = gLibraryList;
+ gLibraryList = e;
+ pthread_mutex_unlock(&gLibLock);
+ ALOGV("getLibrary() linked library %p for path %s", l, path);
+
+ return 0;
+
+error:
+ if (hdl != NULL) {
+ dlclose(hdl);
+ }
+ //add entry for library errors in gLibraryFailedList
+ lib_failed_entry_t *fl = malloc(sizeof(lib_failed_entry_t));
+ fl->name = strndup(name, PATH_MAX);
+ fl->path = strndup(path, PATH_MAX);
+
+ list_elem_t *fe = malloc(sizeof(list_elem_t));
+ fe->object = fl;
+ fe->next = gLibraryFailedList;
+ gLibraryFailedList = fe;
+ ALOGV("getLibrary() linked error in library %p for path %s", fl, path);
+
+ return -EINVAL;
+}
+
+// This will find the library and UUID tags of the sub effect pointed by the
+// node, gets the effect descriptor and lib_entry_t and adds the subeffect -
+// sub_entry_t to the gSubEffectList
+int addSubEffect(cnode *root)
+{
+ ALOGV("addSubEffect");
+ cnode *node;
+ effect_uuid_t uuid;
+ effect_descriptor_t *d;
+ lib_entry_t *l;
+ list_elem_t *e;
+ node = config_find(root, LIBRARY_TAG);
+ if (node == NULL) {
+ return -EINVAL;
+ }
+ l = getLibrary(node->value);
+ if (l == NULL) {
+ ALOGW("addSubEffect() could not get library %s", node->value);
+ return -EINVAL;
+ }
+ node = config_find(root, UUID_TAG);
+ if (node == NULL) {
+ return -EINVAL;
+ }
+ if (stringToUuid(node->value, &uuid) != 0) {
+ ALOGW("addSubEffect() invalid uuid %s", node->value);
+ return -EINVAL;
+ }
+ d = malloc(sizeof(effect_descriptor_t));
+ if (l->desc->get_descriptor(&uuid, d) != 0) {
+ char s[40];
+ uuidToString(&uuid, s, 40);
+ ALOGW("Error querying effect %s on lib %s", s, l->name);
+ free(d);
+ return -EINVAL;
+ }
+#if (LOG_NDEBUG==0)
+ char s[512];
+ dumpEffectDescriptor(d, s, sizeof(s), 0 /* indent */);
+ ALOGV("addSubEffect() read descriptor %p:%s",d, s);
+#endif
+ if (EFFECT_API_VERSION_MAJOR(d->apiVersion) !=
+ EFFECT_API_VERSION_MAJOR(EFFECT_CONTROL_API_VERSION)) {
+ ALOGW("Bad API version %08x on lib %s", d->apiVersion, l->name);
+ free(d);
+ return -EINVAL;
+ }
+ sub_effect_entry_t *sub_effect = malloc(sizeof(sub_effect_entry_t));
+ sub_effect->object = d;
+ // lib_entry_t is stored since the sub effects are not linked to the library
+ sub_effect->lib = l;
+ e = malloc(sizeof(list_elem_t));
+ e->object = sub_effect;
+ e->next = gSubEffectList->sub_elem;
+ gSubEffectList->sub_elem = e;
+ ALOGV("addSubEffect end");
+ return 0;
+}
+
+int loadEffects(cnode *root)
+{
+ cnode *node;
+
+ node = config_find(root, EFFECTS_TAG);
+ if (node == NULL) {
+ return -ENOENT;
+ }
+ node = node->first_child;
+ while (node) {
+ loadEffect(node);
+ node = node->next;
+ }
+ return 0;
+}
+
+int loadEffect(cnode *root)
+{
+ cnode *node;
+ effect_uuid_t uuid;
+ lib_entry_t *l;
+ effect_descriptor_t *d;
+ list_elem_t *e;
+
+ node = config_find(root, LIBRARY_TAG);
+ if (node == NULL) {
+ return -EINVAL;
+ }
+
+ l = getLibrary(node->value);
+ if (l == NULL) {
+ ALOGW("loadEffect() could not get library %s", node->value);
+ return -EINVAL;
+ }
+
+ node = config_find(root, UUID_TAG);
+ if (node == NULL) {
+ return -EINVAL;
+ }
+ if (stringToUuid(node->value, &uuid) != 0) {
+ ALOGW("loadEffect() invalid uuid %s", node->value);
+ return -EINVAL;
+ }
+ lib_entry_t *tmp;
+ bool skip = false;
+ if (findEffect(NULL, &uuid, &tmp, NULL) == 0) {
+ ALOGW("skipping duplicate uuid %s %s", node->value,
+ node->next ? "and its sub-effects" : "");
+ skip = true;
+ }
+
+ d = malloc(sizeof(effect_descriptor_t));
+ if (l->desc->get_descriptor(&uuid, d) != 0) {
+ char s[40];
+ uuidToString(&uuid, s, 40);
+ ALOGW("Error querying effect %s on lib %s", s, l->name);
+ free(d);
+ return -EINVAL;
+ }
+#if (LOG_NDEBUG==0)
+ char s[512];
+ dumpEffectDescriptor(d, s, sizeof(s), 0 /* indent */);
+ ALOGV("loadEffect() read descriptor %p:%s",d, s);
+#endif
+ if (EFFECT_API_VERSION_MAJOR(d->apiVersion) !=
+ EFFECT_API_VERSION_MAJOR(EFFECT_CONTROL_API_VERSION)) {
+ ALOGW("Bad API version %08x on lib %s", d->apiVersion, l->name);
+ free(d);
+ return -EINVAL;
+ }
+ e = malloc(sizeof(list_elem_t));
+ e->object = d;
+ if (skip) {
+ e->next = gSkippedEffects;
+ gSkippedEffects = e;
+ return -EINVAL;
+ } else {
+ e->next = l->effects;
+ l->effects = e;
+ }
+
+ // After the UUID node in the config_tree, if node->next is valid,
+ // that would be sub effect node.
+ // Find the sub effects and add them to the gSubEffectList
+ node = node->next;
+ int count = 2;
+ bool hwSubefx = false, swSubefx = false;
+ list_sub_elem_t *sube = NULL;
+ if (node != NULL) {
+ ALOGV("Adding the effect to gEffectSubList as there are sub effects");
+ sube = malloc(sizeof(list_sub_elem_t));
+ sube->object = d;
+ sube->sub_elem = NULL;
+ sube->next = gSubEffectList;
+ gSubEffectList = sube;
+ }
+ while (node != NULL && count) {
+ if (addSubEffect(node)) {
+ ALOGW("loadEffect() could not add subEffect %s", node->value);
+ // Change the gSubEffectList to point to older list;
+ gSubEffectList = sube->next;
+ free(sube->sub_elem);// Free an already added sub effect
+ sube->sub_elem = NULL;
+ free(sube);
+ return -ENOENT;
+ }
+ sub_effect_entry_t *subEntry = (sub_effect_entry_t*)gSubEffectList->sub_elem->object;
+ effect_descriptor_t *subEffectDesc = (effect_descriptor_t*)(subEntry->object);
+ // Since we return a dummy descriptor for the proxy during
+ // get_descriptor call,we replace it with the correspoding
+ // sw effect descriptor, but with Proxy UUID
+ // check for Sw desc
+ if (!((subEffectDesc->flags & EFFECT_FLAG_HW_ACC_MASK) ==
+ EFFECT_FLAG_HW_ACC_TUNNEL)) {
+ swSubefx = true;
+ *d = *subEffectDesc;
+ d->uuid = uuid;
+ ALOGV("loadEffect() Changed the Proxy desc");
+ } else
+ hwSubefx = true;
+ count--;
+ node = node->next;
+ }
+ // 1 HW and 1 SW sub effect found. Set the offload flag in the Proxy desc
+ if (hwSubefx && swSubefx) {
+ d->flags |= EFFECT_FLAG_OFFLOAD_SUPPORTED;
+ }
+ return 0;
+}
+
+lib_entry_t *getLibrary(const char *name)
+{
+ list_elem_t *e;
+
+ if (gCachedLibrary &&
+ !strncmp(gCachedLibrary->name, name, PATH_MAX)) {
+ return gCachedLibrary;
+ }
+
+ e = gLibraryList;
+ while (e) {
+ lib_entry_t *l = (lib_entry_t *)e->object;
+ if (!strcmp(l->name, name)) {
+ gCachedLibrary = l;
+ return l;
+ }
+ e = e->next;
+ }
+
+ return NULL;
+}
diff --git a/media/libeffects/factory/EffectsConfigLoader.h b/media/libeffects/factory/EffectsConfigLoader.h
new file mode 100644
index 0000000..3f82609
--- /dev/null
+++ b/media/libeffects/factory/EffectsConfigLoader.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_EFFECTSCONFIGLOADER_H
+#define ANDROID_EFFECTSCONFIGLOADER_H
+
+#include <cutils/compiler.h>
+#include "EffectsFactoryState.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/** Parses the platform effect configuration
+ * and stores its content in the global EffectFactoryState. */
+ANDROID_API
+int EffectLoadEffectConfig();
+
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // ANDROID_EFFECTSCONFIGLOADER_H
diff --git a/media/libeffects/factory/EffectsFactory.c b/media/libeffects/factory/EffectsFactory.c
index 37c0bb7..cd0e765 100644
--- a/media/libeffects/factory/EffectsFactory.c
+++ b/media/libeffects/factory/EffectsFactory.c
@@ -17,65 +17,46 @@
#define LOG_TAG "EffectsFactory"
//#define LOG_NDEBUG 0
-#include "EffectsFactory.h"
-
-#include <dlfcn.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#include <cutils/config_utils.h>
-#include <cutils/misc.h>
#include <cutils/properties.h>
#include <log/log.h>
-#include <system/audio_effects/audio_effects_conf.h>
+#include <media/EffectsFactoryApi.h>
+
+#include "EffectsConfigLoader.h"
+#include "EffectsFactoryState.h"
+#include "EffectsXmlConfigLoader.h"
+
+#include "EffectsFactory.h"
static list_elem_t *gEffectList; // list of effect_entry_t: all currently created effects
-static list_elem_t *gLibraryList; // list of lib_entry_t: all currently loaded libraries
-static list_elem_t *gSkippedEffects; // list of effects skipped because of duplicate uuid
-// list of effect_descriptor and list of sub effects : all currently loaded
-// It does not contain effects without sub effects.
-static list_sub_elem_t *gSubEffectList;
-static pthread_mutex_t gLibLock = PTHREAD_MUTEX_INITIALIZER; // controls access to gLibraryList
static uint32_t gNumEffects; // total number number of effects
static list_elem_t *gCurLib; // current library in enumeration process
static list_elem_t *gCurEffect; // current effect in enumeration process
static uint32_t gCurEffectIdx; // current effect index in enumeration process
-static lib_entry_t *gCachedLibrary; // last library accessed by getLibrary()
+/** Number of elements skipped during the effects configuration loading.
+ * -1 if the config loader failed
+ * -2 if config load was skipped
+ */
+static ssize_t gConfigNbElemSkipped = -2;
static int gInitDone; // true is global initialization has been preformed
static int gCanQueryEffect; // indicates that call to EffectQueryEffect() is valid, i.e. that the list of effects
// was not modified since last call to EffectQueryNumberEffects()
-
-static list_elem_t *gLibraryFailedList; //list of lib_failed_entry_t: libraries failed to load
-
/////////////////////////////////////////////////
// Local functions prototypes
/////////////////////////////////////////////////
static int init();
-static int loadEffectConfigFile(const char *path);
-static int loadLibraries(cnode *root);
-static int loadLibrary(cnode *root, const char *name);
-static int loadEffects(cnode *root);
-static int loadEffect(cnode *node);
-// To get and add the effect pointed by the passed node to the gSubEffectList
-static int addSubEffect(cnode *root);
-static lib_entry_t *getLibrary(const char *path);
static void resetEffectEnumeration();
static uint32_t updateNumEffects();
-static int findEffect(const effect_uuid_t *type,
- const effect_uuid_t *uuid,
- lib_entry_t **lib,
- effect_descriptor_t **desc);
// To search a subeffect in the gSubEffectList
static int findSubEffect(const effect_uuid_t *uuid,
lib_entry_t **lib,
effect_descriptor_t **desc);
-static void dumpEffectDescriptor(effect_descriptor_t *desc, char *str, size_t len, int indent);
-static int stringToUuid(const char *str, effect_uuid_t *uuid);
-static int uuidToString(const effect_uuid_t *uuid, char *str, size_t maxLen);
/////////////////////////////////////////////////
// Effect Control Interface functions
@@ -461,10 +442,12 @@
if (ignoreFxConfFiles) {
ALOGI("Audio effects in configuration files will be ignored");
} else {
- if (access(AUDIO_EFFECT_VENDOR_CONFIG_FILE, R_OK) == 0) {
- loadEffectConfigFile(AUDIO_EFFECT_VENDOR_CONFIG_FILE);
- } else if (access(AUDIO_EFFECT_DEFAULT_CONFIG_FILE, R_OK) == 0) {
- loadEffectConfigFile(AUDIO_EFFECT_DEFAULT_CONFIG_FILE);
+ gConfigNbElemSkipped = EffectLoadXmlEffectConfig(NULL);
+ if (gConfigNbElemSkipped < 0) {
+ ALOGW("Failed to load XML effect configuration, fallback to .conf");
+ EffectLoadEffectConfig();
+ } else if (gConfigNbElemSkipped > 0) {
+ ALOGE("Effect config is partially invalid, skipped %zd elements", gConfigNbElemSkipped);
}
}
@@ -474,367 +457,6 @@
return 0;
}
-int loadEffectConfigFile(const char *path)
-{
- cnode *root;
- char *data;
-
- data = load_file(path, NULL);
- if (data == NULL) {
- return -ENODEV;
- }
- root = config_node("", "");
- config_load(root, data);
- loadLibraries(root);
- loadEffects(root);
- config_free(root);
- free(root);
- free(data);
-
- return 0;
-}
-
-int loadLibraries(cnode *root)
-{
- cnode *node;
-
- node = config_find(root, LIBRARIES_TAG);
- if (node == NULL) {
- return -ENOENT;
- }
- node = node->first_child;
- while (node) {
- loadLibrary(node, node->name);
- node = node->next;
- }
- return 0;
-}
-
-#ifdef __LP64__
-// audio_effects.conf always specifies 32 bit lib path: convert to 64 bit path if needed
-static const char *kLibraryPathRoot[] =
- {"/odm/lib64/soundfx", "/vendor/lib64/soundfx", "/system/lib64/soundfx"};
-#else
-static const char *kLibraryPathRoot[] =
- {"/odm/lib/soundfx", "/vendor/lib/soundfx", "/system/lib/soundfx"};
-#endif
-
-static const int kLibraryPathRootSize =
- (sizeof(kLibraryPathRoot) / sizeof(kLibraryPathRoot[0]));
-
-// Checks if the library path passed as lib_path_in can be opened and if not
-// tries in standard effect library directories with just the library name and returns correct path
-// in lib_path_out
-int checkLibraryPath(const char *lib_path_in, char *lib_path_out) {
- char *str;
- const char *lib_name;
- size_t len;
-
- if (lib_path_in == NULL || lib_path_out == NULL) {
- return -EINVAL;
- }
-
- strlcpy(lib_path_out, lib_path_in, PATH_MAX);
-
- // Try exact path first
- str = strstr(lib_path_out, "/lib/soundfx/");
- if (str == NULL) {
- return -EINVAL;
- }
-
- // Extract library name from input path
- len = str - lib_path_out;
- lib_name = lib_path_in + len + strlen("/lib/soundfx/");
-
- // Then try with library name and standard path names in order of preference
- for (int i = 0; i < kLibraryPathRootSize; i++) {
- char path[PATH_MAX];
-
- snprintf(path,
- PATH_MAX,
- "%s/%s",
- kLibraryPathRoot[i],
- lib_name);
- if (F_OK == access(path, 0)) {
- strcpy(lib_path_out, path);
- ALOGW_IF(strncmp(lib_path_out, lib_path_in, PATH_MAX) != 0,
- "checkLibraryPath() corrected library path %s to %s", lib_path_in, lib_path_out);
- return 0;
- }
- }
- return -EINVAL;
-}
-
-
-
-int loadLibrary(cnode *root, const char *name)
-{
- cnode *node;
- void *hdl = NULL;
- audio_effect_library_t *desc;
- list_elem_t *e;
- lib_entry_t *l;
- char path[PATH_MAX];
-
- node = config_find(root, PATH_TAG);
- if (node == NULL) {
- return -EINVAL;
- }
-
- if (checkLibraryPath((const char *)node->value, path) != 0) {
- ALOGW("loadLibrary() could not find library %s", path);
- goto error;
- }
-
- hdl = dlopen(path, RTLD_NOW);
- if (hdl == NULL) {
- ALOGW("loadLibrary() failed to open %s", path);
- goto error;
- }
-
- desc = (audio_effect_library_t *)dlsym(hdl, AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR);
- if (desc == NULL) {
- ALOGW("loadLibrary() could not find symbol %s", AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR);
- goto error;
- }
-
- if (AUDIO_EFFECT_LIBRARY_TAG != desc->tag) {
- ALOGW("getLibrary() bad tag %08x in lib info struct", desc->tag);
- goto error;
- }
-
- if (EFFECT_API_VERSION_MAJOR(desc->version) !=
- EFFECT_API_VERSION_MAJOR(EFFECT_LIBRARY_API_VERSION)) {
- ALOGW("loadLibrary() bad lib version %08x", desc->version);
- goto error;
- }
-
- // add entry for library in gLibraryList
- l = malloc(sizeof(lib_entry_t));
- l->name = strndup(name, PATH_MAX);
- l->path = strndup(path, PATH_MAX);
- l->handle = hdl;
- l->desc = desc;
- l->effects = NULL;
- pthread_mutex_init(&l->lock, NULL);
-
- e = malloc(sizeof(list_elem_t));
- e->object = l;
- pthread_mutex_lock(&gLibLock);
- e->next = gLibraryList;
- gLibraryList = e;
- pthread_mutex_unlock(&gLibLock);
- ALOGV("getLibrary() linked library %p for path %s", l, path);
-
- return 0;
-
-error:
- if (hdl != NULL) {
- dlclose(hdl);
- }
- //add entry for library errors in gLibraryFailedList
- lib_failed_entry_t *fl = malloc(sizeof(lib_failed_entry_t));
- fl->name = strndup(name, PATH_MAX);
- fl->path = strndup(path, PATH_MAX);
-
- list_elem_t *fe = malloc(sizeof(list_elem_t));
- fe->object = fl;
- fe->next = gLibraryFailedList;
- gLibraryFailedList = fe;
- ALOGV("getLibrary() linked error in library %p for path %s", fl, path);
-
- return -EINVAL;
-}
-
-// This will find the library and UUID tags of the sub effect pointed by the
-// node, gets the effect descriptor and lib_entry_t and adds the subeffect -
-// sub_entry_t to the gSubEffectList
-int addSubEffect(cnode *root)
-{
- ALOGV("addSubEffect");
- cnode *node;
- effect_uuid_t uuid;
- effect_descriptor_t *d;
- lib_entry_t *l;
- list_elem_t *e;
- node = config_find(root, LIBRARY_TAG);
- if (node == NULL) {
- return -EINVAL;
- }
- l = getLibrary(node->value);
- if (l == NULL) {
- ALOGW("addSubEffect() could not get library %s", node->value);
- return -EINVAL;
- }
- node = config_find(root, UUID_TAG);
- if (node == NULL) {
- return -EINVAL;
- }
- if (stringToUuid(node->value, &uuid) != 0) {
- ALOGW("addSubEffect() invalid uuid %s", node->value);
- return -EINVAL;
- }
- d = malloc(sizeof(effect_descriptor_t));
- if (l->desc->get_descriptor(&uuid, d) != 0) {
- char s[40];
- uuidToString(&uuid, s, 40);
- ALOGW("Error querying effect %s on lib %s", s, l->name);
- free(d);
- return -EINVAL;
- }
-#if (LOG_NDEBUG==0)
- char s[512];
- dumpEffectDescriptor(d, s, sizeof(s), 0 /* indent */);
- ALOGV("addSubEffect() read descriptor %p:%s",d, s);
-#endif
- if (EFFECT_API_VERSION_MAJOR(d->apiVersion) !=
- EFFECT_API_VERSION_MAJOR(EFFECT_CONTROL_API_VERSION)) {
- ALOGW("Bad API version %08x on lib %s", d->apiVersion, l->name);
- free(d);
- return -EINVAL;
- }
- sub_effect_entry_t *sub_effect = malloc(sizeof(sub_effect_entry_t));
- sub_effect->object = d;
- // lib_entry_t is stored since the sub effects are not linked to the library
- sub_effect->lib = l;
- e = malloc(sizeof(list_elem_t));
- e->object = sub_effect;
- e->next = gSubEffectList->sub_elem;
- gSubEffectList->sub_elem = e;
- ALOGV("addSubEffect end");
- return 0;
-}
-
-int loadEffects(cnode *root)
-{
- cnode *node;
-
- node = config_find(root, EFFECTS_TAG);
- if (node == NULL) {
- return -ENOENT;
- }
- node = node->first_child;
- while (node) {
- loadEffect(node);
- node = node->next;
- }
- return 0;
-}
-
-int loadEffect(cnode *root)
-{
- cnode *node;
- effect_uuid_t uuid;
- lib_entry_t *l;
- effect_descriptor_t *d;
- list_elem_t *e;
-
- node = config_find(root, LIBRARY_TAG);
- if (node == NULL) {
- return -EINVAL;
- }
-
- l = getLibrary(node->value);
- if (l == NULL) {
- ALOGW("loadEffect() could not get library %s", node->value);
- return -EINVAL;
- }
-
- node = config_find(root, UUID_TAG);
- if (node == NULL) {
- return -EINVAL;
- }
- if (stringToUuid(node->value, &uuid) != 0) {
- ALOGW("loadEffect() invalid uuid %s", node->value);
- return -EINVAL;
- }
- lib_entry_t *tmp;
- bool skip = false;
- if (findEffect(NULL, &uuid, &tmp, NULL) == 0) {
- ALOGW("skipping duplicate uuid %s %s", node->value,
- node->next ? "and its sub-effects" : "");
- skip = true;
- }
-
- d = malloc(sizeof(effect_descriptor_t));
- if (l->desc->get_descriptor(&uuid, d) != 0) {
- char s[40];
- uuidToString(&uuid, s, 40);
- ALOGW("Error querying effect %s on lib %s", s, l->name);
- free(d);
- return -EINVAL;
- }
-#if (LOG_NDEBUG==0)
- char s[512];
- dumpEffectDescriptor(d, s, sizeof(s), 0 /* indent */);
- ALOGV("loadEffect() read descriptor %p:%s",d, s);
-#endif
- if (EFFECT_API_VERSION_MAJOR(d->apiVersion) !=
- EFFECT_API_VERSION_MAJOR(EFFECT_CONTROL_API_VERSION)) {
- ALOGW("Bad API version %08x on lib %s", d->apiVersion, l->name);
- free(d);
- return -EINVAL;
- }
- e = malloc(sizeof(list_elem_t));
- e->object = d;
- if (skip) {
- e->next = gSkippedEffects;
- gSkippedEffects = e;
- return -EINVAL;
- } else {
- e->next = l->effects;
- l->effects = e;
- }
-
- // After the UUID node in the config_tree, if node->next is valid,
- // that would be sub effect node.
- // Find the sub effects and add them to the gSubEffectList
- node = node->next;
- int count = 2;
- bool hwSubefx = false, swSubefx = false;
- list_sub_elem_t *sube = NULL;
- if (node != NULL) {
- ALOGV("Adding the effect to gEffectSubList as there are sub effects");
- sube = malloc(sizeof(list_sub_elem_t));
- sube->object = d;
- sube->sub_elem = NULL;
- sube->next = gSubEffectList;
- gSubEffectList = sube;
- }
- while (node != NULL && count) {
- if (addSubEffect(node)) {
- ALOGW("loadEffect() could not add subEffect %s", node->value);
- // Change the gSubEffectList to point to older list;
- gSubEffectList = sube->next;
- free(sube->sub_elem);// Free an already added sub effect
- sube->sub_elem = NULL;
- free(sube);
- return -ENOENT;
- }
- sub_effect_entry_t *subEntry = (sub_effect_entry_t*)gSubEffectList->sub_elem->object;
- effect_descriptor_t *subEffectDesc = (effect_descriptor_t*)(subEntry->object);
- // Since we return a dummy descriptor for the proxy during
- // get_descriptor call,we replace it with the correspoding
- // sw effect descriptor, but with Proxy UUID
- // check for Sw desc
- if (!((subEffectDesc->flags & EFFECT_FLAG_HW_ACC_MASK) ==
- EFFECT_FLAG_HW_ACC_TUNNEL)) {
- swSubefx = true;
- *d = *subEffectDesc;
- d->uuid = uuid;
- ALOGV("loadEffect() Changed the Proxy desc");
- } else
- hwSubefx = true;
- count--;
- node = node->next;
- }
- // 1 HW and 1 SW sub effect found. Set the offload flag in the Proxy desc
- if (hwSubefx && swSubefx) {
- d->flags |= EFFECT_FLAG_OFFLOAD_SUPPORTED;
- }
- return 0;
-}
-
// Searches the sub effect matching to the specified uuid
// in the gSubEffectList. It gets the lib_entry_t for
// the matched sub_effect . Used in EffectCreate of sub effects
@@ -881,29 +503,6 @@
return ret;
}
-lib_entry_t *getLibrary(const char *name)
-{
- list_elem_t *e;
-
- if (gCachedLibrary &&
- !strncmp(gCachedLibrary->name, name, PATH_MAX)) {
- return gCachedLibrary;
- }
-
- e = gLibraryList;
- while (e) {
- lib_entry_t *l = (lib_entry_t *)e->object;
- if (!strcmp(l->name, name)) {
- gCachedLibrary = l;
- return l;
- }
- e = e->next;
- }
-
- return NULL;
-}
-
-
void resetEffectEnumeration()
{
gCurLib = gLibraryList;
@@ -935,114 +534,6 @@
return cnt;
}
-int findEffect(const effect_uuid_t *type,
- const effect_uuid_t *uuid,
- lib_entry_t **lib,
- effect_descriptor_t **desc)
-{
- list_elem_t *e = gLibraryList;
- lib_entry_t *l = NULL;
- effect_descriptor_t *d = NULL;
- int found = 0;
- int ret = 0;
-
- while (e && !found) {
- l = (lib_entry_t *)e->object;
- list_elem_t *efx = l->effects;
- while (efx) {
- d = (effect_descriptor_t *)efx->object;
- if (type != NULL && memcmp(&d->type, type, sizeof(effect_uuid_t)) == 0) {
- found = 1;
- break;
- }
- if (uuid != NULL && memcmp(&d->uuid, uuid, sizeof(effect_uuid_t)) == 0) {
- found = 1;
- break;
- }
- efx = efx->next;
- }
- e = e->next;
- }
- if (!found) {
- ALOGV("findEffect() effect not found");
- ret = -ENOENT;
- } else {
- ALOGV("findEffect() found effect: %s in lib %s", d->name, l->name);
- *lib = l;
- if (desc) {
- *desc = d;
- }
- }
-
- return ret;
-}
-
-void dumpEffectDescriptor(effect_descriptor_t *desc, char *str, size_t len, int indent) {
- char s[256];
- char ss[256];
- char idt[indent + 1];
-
- memset(idt, ' ', indent);
- idt[indent] = 0;
-
- str[0] = 0;
-
- snprintf(s, sizeof(s), "%s%s / %s\n", idt, desc->name, desc->implementor);
- strlcat(str, s, len);
-
- uuidToString(&desc->uuid, s, sizeof(s));
- snprintf(ss, sizeof(ss), "%s UUID: %s\n", idt, s);
- strlcat(str, ss, len);
-
- uuidToString(&desc->type, s, sizeof(s));
- snprintf(ss, sizeof(ss), "%s TYPE: %s\n", idt, s);
- strlcat(str, ss, len);
-
- sprintf(s, "%s apiVersion: %08X\n%s flags: %08X\n", idt,
- desc->apiVersion, idt, desc->flags);
- strlcat(str, s, len);
-}
-
-int stringToUuid(const char *str, effect_uuid_t *uuid)
-{
- int tmp[10];
-
- if (sscanf(str, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
- tmp, tmp+1, tmp+2, tmp+3, tmp+4, tmp+5, tmp+6, tmp+7, tmp+8, tmp+9) < 10) {
- return -EINVAL;
- }
- uuid->timeLow = (uint32_t)tmp[0];
- uuid->timeMid = (uint16_t)tmp[1];
- uuid->timeHiAndVersion = (uint16_t)tmp[2];
- uuid->clockSeq = (uint16_t)tmp[3];
- uuid->node[0] = (uint8_t)tmp[4];
- uuid->node[1] = (uint8_t)tmp[5];
- uuid->node[2] = (uint8_t)tmp[6];
- uuid->node[3] = (uint8_t)tmp[7];
- uuid->node[4] = (uint8_t)tmp[8];
- uuid->node[5] = (uint8_t)tmp[9];
-
- return 0;
-}
-
-int uuidToString(const effect_uuid_t *uuid, char *str, size_t maxLen)
-{
-
- snprintf(str, maxLen, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
- uuid->timeLow,
- uuid->timeMid,
- uuid->timeHiAndVersion,
- uuid->clockSeq,
- uuid->node[0],
- uuid->node[1],
- uuid->node[2],
- uuid->node[3],
- uuid->node[4],
- uuid->node[5]);
-
- return 0;
-}
-
int EffectDumpEffects(int fd) {
char s[512];
@@ -1092,6 +583,20 @@
e = e->next;
}
}
+ switch (gConfigNbElemSkipped) {
+ case -2:
+ dprintf(fd, "Effect configuration loading skipped.\n");
+ break;
+ case -1:
+ dprintf(fd, "XML effect configuration failed to load.\n");
+ break;
+ case 0:
+ dprintf(fd, "XML effect configuration loaded successfully.\n");
+ break;
+ default:
+ dprintf(fd, "XML effect configuration partially loaded, skipped %zd elements.\n",
+ gConfigNbElemSkipped);
+ }
return ret;
}
diff --git a/media/libeffects/factory/EffectsFactory.h b/media/libeffects/factory/EffectsFactory.h
index 72e0931..cdba362 100644
--- a/media/libeffects/factory/EffectsFactory.h
+++ b/media/libeffects/factory/EffectsFactory.h
@@ -21,6 +21,7 @@
#include <pthread.h>
#include <android/log.h>
+#include <cutils/compiler.h>
#include <hardware/audio_effect.h>
#if __cplusplus
@@ -96,6 +97,7 @@
// *pDescriptor: updated with the sub effect descriptors.
//
////////////////////////////////////////////////////////////////////////////////
+ANDROID_API
int EffectGetSubEffects(const effect_uuid_t *pEffectUuid,
sub_effect_entry_t **pSube,
size_t size);
diff --git a/media/libeffects/factory/EffectsFactoryState.c b/media/libeffects/factory/EffectsFactoryState.c
new file mode 100644
index 0000000..b364004
--- /dev/null
+++ b/media/libeffects/factory/EffectsFactoryState.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "EffectsFactoryState"
+
+#include "EffectsFactoryState.h"
+
+#include "log/log.h"
+
+list_elem_t *gLibraryList;
+list_elem_t *gSkippedEffects;
+list_sub_elem_t *gSubEffectList;
+pthread_mutex_t gLibLock = PTHREAD_MUTEX_INITIALIZER;
+
+list_elem_t *gLibraryFailedList; //list of lib_failed_entry_t: libraries failed to load
+
+
+int findEffect(const effect_uuid_t *type,
+ const effect_uuid_t *uuid,
+ lib_entry_t **lib,
+ effect_descriptor_t **desc)
+{
+ list_elem_t *e = gLibraryList;
+ lib_entry_t *l = NULL;
+ effect_descriptor_t *d = NULL;
+ int found = 0;
+ int ret = 0;
+
+ while (e && !found) {
+ l = (lib_entry_t *)e->object;
+ list_elem_t *efx = l->effects;
+ while (efx) {
+ d = (effect_descriptor_t *)efx->object;
+ if (type != NULL && memcmp(&d->type, type, sizeof(effect_uuid_t)) == 0) {
+ found = 1;
+ break;
+ }
+ if (uuid != NULL && memcmp(&d->uuid, uuid, sizeof(effect_uuid_t)) == 0) {
+ found = 1;
+ break;
+ }
+ efx = efx->next;
+ }
+ e = e->next;
+ }
+ if (!found) {
+ ALOGV("findEffect() effect not found");
+ ret = -ENOENT;
+ } else {
+ ALOGV("findEffect() found effect: %s in lib %s", d->name, l->name);
+ *lib = l;
+ if (desc) {
+ *desc = d;
+ }
+ }
+
+ return ret;
+}
+
+int stringToUuid(const char *str, effect_uuid_t *uuid)
+{
+ int tmp[10];
+
+ if (sscanf(str, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
+ tmp, tmp+1, tmp+2, tmp+3, tmp+4, tmp+5, tmp+6, tmp+7, tmp+8, tmp+9) < 10) {
+ return -EINVAL;
+ }
+ uuid->timeLow = (uint32_t)tmp[0];
+ uuid->timeMid = (uint16_t)tmp[1];
+ uuid->timeHiAndVersion = (uint16_t)tmp[2];
+ uuid->clockSeq = (uint16_t)tmp[3];
+ uuid->node[0] = (uint8_t)tmp[4];
+ uuid->node[1] = (uint8_t)tmp[5];
+ uuid->node[2] = (uint8_t)tmp[6];
+ uuid->node[3] = (uint8_t)tmp[7];
+ uuid->node[4] = (uint8_t)tmp[8];
+ uuid->node[5] = (uint8_t)tmp[9];
+
+ return 0;
+}
+
+int uuidToString(const effect_uuid_t *uuid, char *str, size_t maxLen)
+{
+
+ snprintf(str, maxLen, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
+ uuid->timeLow,
+ uuid->timeMid,
+ uuid->timeHiAndVersion,
+ uuid->clockSeq,
+ uuid->node[0],
+ uuid->node[1],
+ uuid->node[2],
+ uuid->node[3],
+ uuid->node[4],
+ uuid->node[5]);
+
+ return 0;
+}
+
+
+void dumpEffectDescriptor(effect_descriptor_t *desc, char *str, size_t len, int indent) {
+ char s[256];
+ char ss[256];
+ char idt[indent + 1];
+
+ memset(idt, ' ', indent);
+ idt[indent] = 0;
+
+ str[0] = 0;
+
+ snprintf(s, sizeof(s), "%s%s / %s\n", idt, desc->name, desc->implementor);
+ strlcat(str, s, len);
+
+ uuidToString(&desc->uuid, s, sizeof(s));
+ snprintf(ss, sizeof(ss), "%s UUID: %s\n", idt, s);
+ strlcat(str, ss, len);
+
+ uuidToString(&desc->type, s, sizeof(s));
+ snprintf(ss, sizeof(ss), "%s TYPE: %s\n", idt, s);
+ strlcat(str, ss, len);
+
+ sprintf(s, "%s apiVersion: %08X\n%s flags: %08X\n", idt,
+ desc->apiVersion, idt, desc->flags);
+ strlcat(str, s, len);
+}
diff --git a/media/libeffects/factory/EffectsFactoryState.h b/media/libeffects/factory/EffectsFactoryState.h
new file mode 100644
index 0000000..aef945e
--- /dev/null
+++ b/media/libeffects/factory/EffectsFactoryState.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_EFFECTSFACTORYSTATE_H_
+#define ANDROID_EFFECTSFACTORYSTATE_H_
+
+#include "EffectsFactory.h"
+
+#if __cplusplus
+extern "C" {
+#endif
+
+/** @file Contains the state shared with configuration loader of the Effect factory.
+ * This global state should probably be refactor in a structure
+ * provided by the config loader on EffectsFactory init.
+ * This header also contains some helper functions to work on the state.
+ */
+
+extern list_elem_t *gLibraryList; // list of lib_entry_t: all currently loaded libraries
+// list of effects skipped because of duplicate uuid or invalid version
+extern list_elem_t *gSkippedEffects;
+// list of effect_descriptor and list of sub effects : all currently loaded
+// It does not contain effects without sub effects.
+extern list_sub_elem_t *gSubEffectList;
+extern pthread_mutex_t gLibLock; // controls access to gLibraryList
+
+extern list_elem_t *gLibraryFailedList; //list of lib_failed_entry_t: libraries failed to load
+
+
+
+int findEffect(const effect_uuid_t *type,
+ const effect_uuid_t *uuid,
+ lib_entry_t **lib,
+ effect_descriptor_t **desc);
+
+int stringToUuid(const char *str, effect_uuid_t *uuid);
+/** Used to log UUIDs */
+int uuidToString(const effect_uuid_t *uuid, char *str, size_t maxLen);
+
+/** Used for debuging. */
+void dumpEffectDescriptor(effect_descriptor_t *desc, char *str, size_t len, int indent);
+
+#if __cplusplus
+} // extern "C"
+#endif
+
+#endif // ANDROID_EFFECTSFACTORYSTATE_H_
diff --git a/media/libeffects/factory/EffectsXmlConfigLoader.cpp b/media/libeffects/factory/EffectsXmlConfigLoader.cpp
new file mode 100644
index 0000000..42e00f8
--- /dev/null
+++ b/media/libeffects/factory/EffectsXmlConfigLoader.cpp
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "EffectsFactoryConfigLoader"
+//#define LOG_NDEBUG 0
+
+#include <dlfcn.h>
+#include <set>
+#include <stdlib.h>
+#include <string>
+
+#include <log/log.h>
+
+#include <media/EffectsConfig.h>
+
+#include "EffectsConfigLoader.h"
+#include "EffectsFactoryState.h"
+#include "EffectsXmlConfigLoader.h"
+
+namespace android {
+
+using namespace effectsConfig;
+
+/////////////////////////////////////////////////
+// Local functions
+/////////////////////////////////////////////////
+
+namespace {
+
+/** Similarly to dlopen, looks for the provided path in LD_EFFECT_LIBRARY_PATH.
+ * @return true if the library is found and set resolvedPath to its absolute path.
+ * false if not found
+ */
+bool resolveLibrary(const std::string& path, std::string* resolvedPath) {
+ for (auto* libraryDirectory : LD_EFFECT_LIBRARY_PATH) {
+ std::string candidatePath = std::string(libraryDirectory) + '/' + path;
+ if (access(candidatePath.c_str(), R_OK) == 0) {
+ *resolvedPath = std::move(candidatePath);
+ return true;
+ }
+ }
+ return false;
+}
+
+/** Loads a library given its relative path and stores the result in libEntry.
+ * @return true on success with libEntry's path, handle and desc filled
+ * false on success with libEntry's path filled with the path of the failed lib
+ * The caller MUST free the resources path (free) and handle (dlclose) if filled.
+ */
+bool loadLibrary(const char* relativePath, lib_entry_t* libEntry) noexcept {
+
+ std::string absolutePath;
+ if (!resolveLibrary(relativePath, &absolutePath)) {
+ ALOGE("Could not find library in effect directories: %s", relativePath);
+ libEntry->path = strdup(relativePath);
+ return false;
+ }
+ const char* path = absolutePath.c_str();
+ libEntry->path = strdup(path);
+
+ // Make sure the lib is closed on early return
+ std::unique_ptr<void, decltype(dlclose)*> libHandle(dlopen(path, RTLD_NOW),
+ dlclose);
+ if (libHandle == nullptr) {
+ ALOGE("Could not dlopen library %s: %s", path, dlerror());
+ return false;
+ }
+
+ auto* description = static_cast<audio_effect_library_t*>(
+ dlsym(libHandle.get(), AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR));
+ if (description == nullptr) {
+ ALOGE("Invalid effect library, failed not find symbol '%s' in %s: %s",
+ AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR, path, dlerror());
+ return false;
+ }
+
+ if (description->tag != AUDIO_EFFECT_LIBRARY_TAG) {
+ ALOGE("Bad tag %#08x in description structure, expected %#08x for library %s",
+ description->tag, AUDIO_EFFECT_LIBRARY_TAG, path);
+ return false;
+ }
+
+ uint32_t majorVersion = EFFECT_API_VERSION_MAJOR(description->version);
+ uint32_t expectedMajorVersion = EFFECT_API_VERSION_MAJOR(EFFECT_LIBRARY_API_VERSION);
+ if (majorVersion != expectedMajorVersion) {
+ ALOGE("Unsupported major version %#08x, expected %#08x for library %s",
+ majorVersion, expectedMajorVersion, path);
+ return false;
+ }
+
+ libEntry->handle = libHandle.release();
+ libEntry->desc = description;
+ return true;
+}
+
+/** Because the structures will be destroyed by c code, using new to allocate shared structure
+ * is not possible. Provide a equivalent of unique_ptr for malloc/freed structure to make sure
+ * they are not leaked in the c++ code.
+ @{ */
+struct FreeDeleter {
+ void operator()(void* p) {
+ free(p);
+ }
+};
+/** unique_ptr for object created with malloc. */
+template <class T>
+using UniqueCPtr = std::unique_ptr<T, FreeDeleter>;
+
+/** c version of std::make_unique. Uses malloc and free. */
+template <class T>
+UniqueCPtr<T> makeUniqueC() {
+ T* ptr = new (malloc(sizeof(T))) T{}; // Use placement new to initialize the structure
+ return UniqueCPtr<T>{ptr};
+}
+
+/** @} */
+
+/** Push an not owned element in a list_elem link list with an optional lock. */
+template <class T, class ListElem>
+void listPush(T* object, ListElem** list, pthread_mutex_t* mutex = nullptr) noexcept {
+ auto listElem = makeUniqueC<ListElem>();
+ listElem->object = object;
+ if (mutex != nullptr) {
+ pthread_mutex_lock(mutex);
+ }
+ listElem->next = *list;
+ *list = listElem.release();
+ if (mutex != nullptr) {
+ pthread_mutex_unlock(mutex);
+ }
+}
+
+/** Push an owned element in a list_elem link list with an optional lock. */
+template <class T, class ListElem>
+void listPush(UniqueCPtr<T>&& object, ListElem** list, pthread_mutex_t* mutex = nullptr) noexcept {
+ listPush(object.release(), list, mutex);
+}
+
+size_t loadLibraries(const effectsConfig::Libraries& libs,
+ list_elem_t** libList, pthread_mutex_t* libListLock,
+ list_elem_t** libFailedList)
+{
+ size_t nbSkippedElement = 0;
+ for (auto& library : libs) {
+
+ // Construct a lib entry
+ auto libEntry = makeUniqueC<lib_entry_t>();
+ libEntry->name = strdup(library.name.c_str());
+ libEntry->effects = nullptr;
+ pthread_mutex_init(&libEntry->lock, nullptr);
+
+ if (!loadLibrary(library.path.c_str(), libEntry.get())) {
+ // Register library load failure
+ listPush(std::move(libEntry), libFailedList);
+ ++nbSkippedElement;
+ continue;
+ }
+ listPush(std::move(libEntry), libList, libListLock);
+ }
+ return nbSkippedElement;
+}
+
+/** Find a library with the given name in the given list. */
+lib_entry_t* findLibrary(const char* name, list_elem_t* list) {
+
+ while (list != nullptr) {
+ auto* object = static_cast<lib_entry_t*>(list->object);
+ if (strcmp(object->name, name) == 0) {
+ return object;
+ }
+ list = list->next;
+ }
+ return nullptr;
+}
+
+struct UuidStr {
+ /** Length of an uuid represented as string. @TODO: use a constant instead of 40. */
+ char buff[40];
+};
+
+/** @return a string representing the provided uuid.
+ * By not providing an output buffer, it is implicitly created in the caller context.
+ * In such case the return pointer has the same lifetime as the expression containing uuidToString()
+ */
+char* uuidToString(const effect_uuid_t& uuid, UuidStr&& str = {}) {
+ uuidToString(&uuid, str.buff, sizeof(str.buff));
+ return str.buff;
+}
+
+struct LoadEffectResult {
+ /** true if the effect is usable (aka, existing lib, desc, right version, unique uuid) */
+ bool success = false;
+ /** Set if the effect lib was found*/
+ lib_entry_t* lib = nullptr;
+ //* Set if the description was successfuly retrieved from the lib */
+ UniqueCPtr<effect_descriptor_t> effectDesc;
+};
+
+LoadEffectResult loadEffect(const EffectImpl& effect, const std::string& name,
+ list_elem_t* libList) {
+ LoadEffectResult result;
+
+ // Find the effect library
+ result.lib = findLibrary(effect.library->name.c_str(), libList);
+ if (result.lib == nullptr) {
+ ALOGE("Could not find library %s to load effect %s",
+ effect.library->name.c_str(), name.c_str());
+ return result;
+ }
+
+ result.effectDesc = makeUniqueC<effect_descriptor_t>();
+
+ // Get the effect descriptor
+ if (result.lib->desc->get_descriptor(&effect.uuid, result.effectDesc.get()) != 0) {
+ ALOGE("Error querying effect %s on lib %s",
+ uuidToString(effect.uuid), result.lib->name);
+ result.effectDesc.reset();
+ return result;
+ }
+
+ // Dump effect for debug
+#if (LOG_NDEBUG==0)
+ char s[512];
+ dumpEffectDescriptor(result.effectDesc.get(), s, sizeof(s), 0 /* indent */);
+ ALOGV("loadEffect() read descriptor %p:%s", result.effectDesc.get(), s);
+#endif
+
+ // Check effect is supported
+ uint32_t expectedMajorVersion = EFFECT_API_VERSION_MAJOR(EFFECT_CONTROL_API_VERSION);
+ if (EFFECT_API_VERSION_MAJOR(result.effectDesc->apiVersion) != expectedMajorVersion) {
+ ALOGE("Bad API version %#08x for effect %s in lib %s, expected major %#08x",
+ result.effectDesc->apiVersion, name.c_str(), result.lib->name, expectedMajorVersion);
+ return result;
+ }
+
+ lib_entry_t *_;
+ if (findEffect(nullptr, &effect.uuid, &_, nullptr) == 0) {
+ ALOGE("Effect %s uuid %s already exist", uuidToString(effect.uuid), name.c_str());
+ return result;
+ }
+
+ result.success = true;
+ return result;
+}
+
+size_t loadEffects(const Effects& effects, list_elem_t* libList, list_elem_t** skippedEffects,
+ list_sub_elem_t** subEffectList) {
+ size_t nbSkippedElement = 0;
+
+ for (auto& effect : effects) {
+
+ auto effectLoadResult = loadEffect(effect, effect.name, libList);
+ if (!effectLoadResult.success) {
+ if (effectLoadResult.effectDesc != nullptr) {
+ listPush(std::move(effectLoadResult.effectDesc), skippedEffects);
+ }
+ ++nbSkippedElement;
+ continue;
+ }
+
+ if (effect.isProxy) {
+ auto swEffectLoadResult = loadEffect(effect.libSw, effect.name + " libsw", libList);
+ auto hwEffectLoadResult = loadEffect(effect.libHw, effect.name + " libhw", libList);
+ if (!swEffectLoadResult.success || !hwEffectLoadResult.success) {
+ // Push the main effect in the skipped list even if only a subeffect is invalid
+ // as the main effect is not usable without its subeffects.
+ listPush(std::move(effectLoadResult.effectDesc), skippedEffects);
+ ++nbSkippedElement;
+ continue;
+ }
+ listPush(&effectLoadResult.effectDesc, subEffectList);
+
+ // Since we return a dummy descriptor for the proxy during
+ // get_descriptor call, we replace it with the corresponding
+ // sw effect descriptor, but keep the Proxy UUID
+ *effectLoadResult.effectDesc = *swEffectLoadResult.effectDesc;
+ effectLoadResult.effectDesc->uuid = effect.uuid;
+
+ effectLoadResult.effectDesc->flags |= EFFECT_FLAG_OFFLOAD_SUPPORTED;
+
+ auto registerSubEffect = [subEffectList](auto&& result) {
+ auto entry = makeUniqueC<sub_effect_entry_t>();
+ entry->object = result.effectDesc.release();
+ // lib_entry_t is stored since the sub effects are not linked to the library
+ entry->lib = result.lib;
+ listPush(std::move(entry), &(*subEffectList)->sub_elem);
+ };
+ registerSubEffect(std::move(swEffectLoadResult));
+ registerSubEffect(std::move(hwEffectLoadResult));
+ }
+
+ listPush(std::move(effectLoadResult.effectDesc), &effectLoadResult.lib->effects);
+ }
+ return nbSkippedElement;
+}
+
+} // namespace
+
+/////////////////////////////////////////////////
+// Interface function
+/////////////////////////////////////////////////
+
+extern "C" ssize_t EffectLoadXmlEffectConfig(const char* path)
+{
+ using effectsConfig::parse;
+ auto result = path ? parse(path) : parse();
+ if (result.parsedConfig == nullptr) {
+ ALOGE("Failed to parse XML configuration file");
+ return -1;
+ }
+ result.nbSkippedElement += loadLibraries(result.parsedConfig->libraries,
+ &gLibraryList, &gLibLock, &gLibraryFailedList) +
+ loadEffects(result.parsedConfig->effects, gLibraryList,
+ &gSkippedEffects, &gSubEffectList);
+
+ ALOGE_IF(result.nbSkippedElement != 0, "%zu errors during loading of configuration: %s",
+ result.nbSkippedElement, path ?: effectsConfig::DEFAULT_PATH);
+
+ return result.nbSkippedElement;
+}
+
+} // namespace android
diff --git a/media/libeffects/factory/EffectsXmlConfigLoader.h b/media/libeffects/factory/EffectsXmlConfigLoader.h
new file mode 100644
index 0000000..a3fe9a3
--- /dev/null
+++ b/media/libeffects/factory/EffectsXmlConfigLoader.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_EFFECTSXMLCONFIGLOADER_H
+#define ANDROID_EFFECTSXMLCONFIGLOADER_H
+
+#include <unistd.h>
+
+#include <cutils/compiler.h>
+
+#include "EffectsFactoryState.h"
+
+#if __cplusplus
+extern "C" {
+#endif
+
+/** Parses the platform effect xml configuration and stores its content in EffectFactoryState.
+ * @param[in] path of the configuration file or NULL to load the default one
+ * @return -1 on unrecoverable error (eg: no configuration file)
+ * 0 on success
+ * the number of invalid elements (lib & effect) skipped if the config is partially invalid
+ * @note this function is exported for test purpose only. Do not call from outside this library.
+ */
+ANDROID_API
+ssize_t EffectLoadXmlEffectConfig(const char* path);
+
+#if __cplusplus
+} // extern "C"
+#endif
+
+#endif // ANDROID_EFFECTSXMLCONFIGLOADER_H
diff --git a/media/libeffects/factory/include/media/EffectsFactoryApi.h b/media/libeffects/factory/include/media/EffectsFactoryApi.h
index 64a3212..a5a12eb 100644
--- a/media/libeffects/factory/include/media/EffectsFactoryApi.h
+++ b/media/libeffects/factory/include/media/EffectsFactoryApi.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_EFFECTSFACTORYAPI_H_
#define ANDROID_EFFECTSFACTORYAPI_H_
+#include <cutils/compiler.h>
#include <errno.h>
#include <stdint.h>
#include <sys/types.h>
@@ -52,6 +53,7 @@
// *pNumEffects: updated with number of effects in factory
//
////////////////////////////////////////////////////////////////////////////////
+ANDROID_API
int EffectQueryNumberEffects(uint32_t *pNumEffects);
////////////////////////////////////////////////////////////////////////////////
@@ -79,6 +81,7 @@
// *pDescriptor: updated with the effect descriptor.
//
////////////////////////////////////////////////////////////////////////////////
+ANDROID_API
int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescriptor);
////////////////////////////////////////////////////////////////////////////////
@@ -110,6 +113,7 @@
// *pHandle: updated with the effect handle.
//
////////////////////////////////////////////////////////////////////////////////
+ANDROID_API
int EffectCreate(const effect_uuid_t *pEffectUuid, int32_t sessionId, int32_t ioId,
effect_handle_t *pHandle);
@@ -130,6 +134,7 @@
// -EINVAL invalid interface handle
//
////////////////////////////////////////////////////////////////////////////////
+ANDROID_API
int EffectRelease(effect_handle_t handle);
////////////////////////////////////////////////////////////////////////////////
@@ -153,6 +158,7 @@
// *pDescriptor: updated with the effect descriptor.
//
////////////////////////////////////////////////////////////////////////////////
+ANDROID_API
int EffectGetDescriptor(const effect_uuid_t *pEffectUuid, effect_descriptor_t *pDescriptor);
////////////////////////////////////////////////////////////////////////////////
@@ -169,8 +175,10 @@
// 1 if uuid is equal to EFFECT_UUID_NULL.
//
////////////////////////////////////////////////////////////////////////////////
+ANDROID_API
int EffectIsNullUuid(const effect_uuid_t *pEffectUuid);
+ANDROID_API
int EffectDumpEffects(int fd);
#if __cplusplus
diff --git a/media/libeffects/factory/test/DumpConfig.cpp b/media/libeffects/factory/test/DumpConfig.cpp
new file mode 100644
index 0000000..0a156b4
--- /dev/null
+++ b/media/libeffects/factory/test/DumpConfig.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <media/EffectsFactoryApi.h>
+#include <unistd.h>
+#include "EffectsXmlConfigLoader.h"
+#include "EffectsConfigLoader.h"
+
+int main(int argc, char* argv[]) {
+ const char* path = nullptr;
+ bool legacyFormat;
+
+ if (argc == 2 && strcmp(argv[1], "--legacy") == 0) {
+ legacyFormat = true;
+ fprintf(stderr, "Dumping legacy effect config file\n");
+ } else if ((argc == 2 || argc == 3) && strcmp(argv[1], "--xml") == 0) {
+ legacyFormat = false;
+ if (argc == 3) {
+ fprintf(stderr, "Dumping XML effect config file: %s\n", path);
+ } else {
+ fprintf(stderr, "Dumping default XML effect config file.\n");
+ }
+ } else {
+ fprintf(stderr, "Invalid arguments.\n"
+ "Usage: %s [--legacy|--xml [FILE]]\n", argv[0]);
+ return 1;
+ }
+
+ if (!legacyFormat) {
+ ssize_t ret = EffectLoadXmlEffectConfig(path);
+ if (ret < 0) {
+ fprintf(stderr, "loadXmlEffectConfig failed, see logcat for detail.\n");
+ return 2;
+ }
+ if (ret > 0) {
+ fprintf(stderr, "Partially failed to load config. Skipped %zu elements, "
+ "see logcat for detail.\n", (size_t)ret);
+ }
+ }
+
+ if (legacyFormat) {
+ auto ret = EffectLoadEffectConfig();
+ if (ret < 0) {
+ fprintf(stderr, "loadEffectConfig failed, see logcat for detail.\n");
+ return 3;
+ }
+ fprintf(stderr, "legacy loadEffectConfig has probably succeed, see logcat to make sure.\n");
+ }
+
+ if (EffectDumpEffects(STDOUT_FILENO) != 0) {
+ fprintf(stderr, "Effect dump failed, see logcat for detail.\n");
+ return 4;
+ }
+}
diff --git a/media/libnbaio/NBLog.cpp b/media/libnbaio/NBLog.cpp
index e3f4876..2b5b4ff 100644
--- a/media/libnbaio/NBLog.cpp
+++ b/media/libnbaio/NBLog.cpp
@@ -892,23 +892,14 @@
ofs.close();
}
-// converts a time series into a map. key: buffer period length. value: count
-static std::map<int, int> buildBuckets(const std::vector<int64_t> &samples) {
- // TODO allow buckets of variable resolution
- std::map<int, int> buckets;
- for (size_t i = 1; i < samples.size(); ++i) {
- ++buckets[deltaMs(samples[i - 1], samples[i])];
- }
- return buckets;
-}
-
void NBLog::Reader::dump(int fd, size_t indent, NBLog::Reader::Snapshot &snapshot)
{
// CallStack cs(LOG_TAG);
mFd = fd;
mIndent = indent;
String8 timestamp, body;
- PerformanceAnalysis performanceAnalyzer; // used to call analysis functions
+ // FIXME: this is not thread safe
+ static PerformanceAnalysis performanceAnalysis; // used to store data and to call analysis functions
size_t lost = snapshot.lost() + (snapshot.begin() - EntryIterator(snapshot.data()));
if (lost > 0) {
body.appendFormat("warning: lost %zu bytes worth of events", lost);
@@ -930,29 +921,7 @@
memcpy(&hash, &(data->hash), sizeof(hash));
int64_t ts;
memcpy(&ts, &data->ts, sizeof(ts));
- const std::pair<log_hash_t, int> key(hash, data->author);
- // TODO might want to filter excessively high outliers, which are usually caused
- // by the thread being inactive.
- mHists[key].push_back(ts);
- // store time series data for each reader in order to bucket it once there
- // is enough data. Then, it is written to recentHists as a histogram.
- mTimeStampSeries[data->author].push_back(ts);
- // if length of the time series has reached kShortHistSize samples, do 1) and 2):
- if (mTimeStampSeries[data->author].size() >= kShortHistSize) {
- // 1) analyze the series to store all outliers and their exact timestamps:
- performanceAnalyzer.storeOutlierData(data->author, mTimeStampSeries[data->author]);
- // 2) compute its histogram, append this to mRecentHists and erase the time series
- mRecentHists.emplace_front(data->author,
- buildBuckets(mTimeStampSeries[data->author]));
- // do not let mRecentHists exceed capacity
- // TODO: turn the FIFO queue into a circular buffer
- if (mRecentHists.size() >= kRecentHistsCapacity) {
- mRecentHists.pop_back();
- }
- mTimeStampSeries.erase(data->author);
- }
- // if an element in mHists has not grown for a long time, delete
- // TODO copy histogram data only to mRecentHistsBuffer and pop oldest
+ performanceAnalysis.logTsEntry(data->author, ts);
++entry;
break;
}
@@ -967,12 +936,10 @@
break;
}
}
- performanceAnalyzer.reportPerformance(&body, mRecentHists);
+ performanceAnalysis.reportPerformance(&body);
if (!body.isEmpty()) {
dumpLine(timestamp, body);
}
- // comment in for tests
- // performanceAnalyzer.testFunction();
}
void NBLog::Reader::dump(int fd, size_t indent)
diff --git a/media/libnbaio/PerformanceAnalysis.cpp b/media/libnbaio/PerformanceAnalysis.cpp
index 84b7635..fa8f47e 100644
--- a/media/libnbaio/PerformanceAnalysis.cpp
+++ b/media/libnbaio/PerformanceAnalysis.cpp
@@ -45,8 +45,21 @@
namespace android {
-PerformanceAnalysis::PerformanceAnalysis() : findGlitch(false) {
- kPeriodMsCPU = static_cast<int>(PerformanceAnalysis::kPeriodMs * kRatio);
+PerformanceAnalysis::PerformanceAnalysis() {
+ // These variables will be (FIXME) learned from the data
+ kPeriodMs = 4; // typical buffer period (mode)
+ // average number of Ms spent processing buffer
+ kPeriodMsCPU = static_cast<int>(kPeriodMs * kRatio);
+}
+
+// converts a time series into a map. key: buffer period length. value: count
+static std::map<int, int> buildBuckets(const std::vector<int64_t> &samples) {
+ // TODO allow buckets of variable resolution
+ std::map<int, int> buckets;
+ for (size_t i = 1; i < samples.size(); ++i) {
+ ++buckets[deltaMs(samples[i - 1], samples[i])];
+ }
+ return buckets;
}
static int widthOf(int x) {
@@ -58,76 +71,164 @@
return width;
}
+// Takes a single buffer period timestamp entry with author information and stores it
+// in a temporary series of timestamps. Once the series is full, the data is analyzed,
+// stored, and emptied.
+// TODO: decide whether author or file location information is more important to store
+// for now, only stores author (thread)
+void PerformanceAnalysis::logTsEntry(int author, int64_t ts) {
+ // TODO might want to filter excessively high outliers, which are usually caused
+ // by the thread being inactive.
+ // Store time series data for each reader in order to bucket it once there
+ // is enough data. Then, write to recentHists as a histogram.
+ mTimeStampSeries[author].push_back(ts);
+ // if length of the time series has reached kShortHistSize samples, do 1) and 2):
+ if (mTimeStampSeries[author].size() >= kShortHistSize) {
+ // 1) analyze the series to store all outliers and their exact timestamps:
+ storeOutlierData(mTimeStampSeries[author]);
+ // 2) detect peaks in the outlier series
+ detectPeaks();
+ // 3) compute its histogram, append this to mRecentHists and erase the time series
+ // FIXME: need to store the timestamp of the beginning of each histogram
+ // FIXME: Restore LOG_HIST_FLUSH to separate histograms at every end-of-stream event
+ // A histogram should not span data between audio off/on timespans
+ mRecentHists.emplace_back(author,
+ buildBuckets(mTimeStampSeries[author]));
+ // do not let mRecentHists exceed capacity
+ // ALOGD("mRecentHists size: %d", static_cast<int>(mRecentHists.size()));
+ if (mRecentHists.size() >= kRecentHistsCapacity) {
+ // ALOGD("popped back mRecentHists");
+ mRecentHists.pop_front();
+ }
+ mTimeStampSeries[author].clear();
+ }
+}
+
+// Given a series of outlier intervals (mOutlier data),
+// looks for changes in distribution (peaks), which can be either positive or negative.
+// The function sets the mean to the starting value and sigma to 0, and updates
+// them as long as no peak is detected. When a value is more than 'threshold'
+// standard deviations from the mean, a peak is detected and the mean and sigma
+// are set to the peak value and 0.
+void PerformanceAnalysis::detectPeaks() {
+ if (mOutlierData.empty()) {
+ ALOGD("peak detector called on empty array");
+ return;
+ }
+
+ // compute mean of the distribution. Used to check whether a value is large
+ const double kTypicalDiff = std::accumulate(
+ mOutlierData.begin(), mOutlierData.end(), 0,
+ [](auto &a, auto &b){return a + b.first;}) / mOutlierData.size();
+ // ALOGD("typicalDiff %f", kTypicalDiff);
+
+ // iterator at the beginning of a sequence, or updated to the most recent peak
+ std::deque<std::pair<uint64_t, uint64_t>>::iterator start = mOutlierData.begin();
+ // the mean and standard deviation are updated every time a peak is detected
+ // initialize first time. The mean from the previous sequence is stored
+ // for the next sequence. Here, they are initialized for the first time.
+ if (mPeakDetectorMean < 0) {
+ mPeakDetectorMean = static_cast<double>(start->first);
+ mPeakDetectorSd = 0;
+ }
+ auto sqr = [](auto x){ return x * x; };
+ for (auto it = mOutlierData.begin(); it != mOutlierData.end(); ++it) {
+ // no surprise occurred:
+ // the new element is a small number of standard deviations from the mean
+ if ((fabs(it->first - mPeakDetectorMean) < kStddevThreshold * mPeakDetectorSd) ||
+ // or: right after peak has been detected, the delta is smaller than average
+ (mPeakDetectorSd == 0 && fabs(it->first - mPeakDetectorMean) < kTypicalDiff)) {
+ // update the mean and sd:
+ // count number of elements (distance between start interator and current)
+ const int kN = std::distance(start, it) + 1;
+ // usual formulas for mean and sd
+ mPeakDetectorMean = std::accumulate(start, it + 1, 0.0,
+ [](auto &a, auto &b){return a + b.first;}) / kN;
+ mPeakDetectorSd = sqrt(std::accumulate(start, it + 1, 0.0,
+ [=](auto &a, auto &b){ return a + sqr(b.first - mPeakDetectorMean);})) /
+ ((kN > 1)? kN - 1 : kN); // kN - 1: mean is correlated with variance
+ // ALOGD("value, mean, sd: %f, %f, %f", static_cast<double>(it->first), mean, sd);
+ }
+ // surprising value: store peak timestamp and reset mean, sd, and start iterator
+ else {
+ mPeakTimestamps.emplace_back(it->second);
+ // TODO: remove pop_front once a circular buffer is in place
+ if (mPeakTimestamps.size() >= kShortHistSize) {
+ ALOGD("popped back mPeakTimestamps");
+ mPeakTimestamps.pop_front();
+ }
+ mPeakDetectorMean = static_cast<double>(it->first);
+ mPeakDetectorSd = 0;
+ start = it;
+ }
+ }
+ //for (const auto &it : mPeakTimestamps) {
+ // ALOGE("mPeakTimestamps %f", static_cast<double>(it));
+ //}
+ return;
+}
+
+// Called by LogTsEntry. The input is a vector of timestamps.
+// Finds outliers and writes to mOutlierdata.
+// Each value in mOutlierdata consists of: <outlier timestamp, time elapsed since previous outlier>.
+// e.g. timestamps (ms) 1, 4, 5, 16, 18, 28 will produce pairs (4, 5), (13, 18).
+// This function is applied to the time series before it is converted into a histogram.
+void PerformanceAnalysis::storeOutlierData(const std::vector<int64_t> ×tamps) {
+ if (timestamps.size() < 1) {
+ ALOGE("storeOutlierData called on empty vector");
+ return;
+ }
+ // first pass: need to initialize
+ if (mElapsed == 0) {
+ mPrevNs = timestamps[0];
+ }
+ for (const auto &ts: timestamps) {
+ const uint64_t diffMs = static_cast<uint64_t>(deltaMs(mPrevNs, ts));
+ if (diffMs >= static_cast<uint64_t>(kOutlierMs)) {
+ mOutlierData.emplace_back(mElapsed, static_cast<uint64_t>(mPrevNs));
+ // Remove oldest value if the vector is full
+ // TODO: remove pop_front once circular buffer is in place
+ // FIXME: change kShortHistSize to some other constant. Make sure it is large
+ // enough that data will never be lost before being written to a long-term FIFO
+ if (mOutlierData.size() >= kShortHistSize) {
+ ALOGD("popped back mOutlierData");
+ mOutlierData.pop_front();
+ }
+ mElapsed = 0;
+ }
+ mElapsed += diffMs;
+ mPrevNs = ts;
+ }
+}
+
+
// FIXME: delete this temporary test code, recycled for various new functions
void PerformanceAnalysis::testFunction() {
// produces values (4: 5000000), (13: 18000000)
// ns timestamps of buffer periods
const std::vector<int64_t>kTempTestData = {1000000, 4000000, 5000000,
16000000, 18000000, 28000000};
- const int kTestAuthor = 1;
- PerformanceAnalysis::storeOutlierData(kTestAuthor, kTempTestData);
+ PerformanceAnalysis::storeOutlierData(kTempTestData);
for (const auto &outlier: mOutlierData) {
ALOGE("PerformanceAnalysis test %lld: %lld",
static_cast<long long>(outlier.first), static_cast<long long>(outlier.second));
}
+ detectPeaks();
}
-// Each pair consists of: <outlier timestamp, time elapsed since previous outlier>.
-// The timestamp of the beginning of the outlier is recorded.
-// The elapsed time is from the timestamp of the previous outlier
-// e.g. timestamps (ms) 1, 4, 5, 16, 18, 28 will produce pairs (4, 5), (13, 18).
-// This function is applied to the time series before it is converted into a histogram.
-void PerformanceAnalysis::storeOutlierData(
- int author, const std::vector<int64_t> ×tamps) {
- if (timestamps.size() < 1) {
- ALOGE("storeOutlierData called on empty vector");
- return;
- }
- author++; // temp to avoid unused error until this value is
- // either TODO: used or discarded from the arglist
- author--;
- uint64_t elapsed = 0;
- int64_t prev = timestamps.at(0);
- for (const auto &ts: timestamps) {
- const uint64_t diff = static_cast<uint64_t>(deltaMs(prev, ts));
- if (diff >= static_cast<uint64_t>(kOutlierMs)) {
- mOutlierData.emplace_back(elapsed, static_cast<uint64_t>(prev));
- elapsed = 0;
- }
- elapsed += diff;
- prev = ts;
- }
- // ALOGE("storeOutlierData: result length %zu", outlierData.size());
- // for (const auto &outlier: OutlierData) {
- // ALOGE("PerformanceAnalysis test %lld: %lld",
- // static_cast<long long>(outlier.first), static_cast<long long>(outlier.second));
- //}
-}
-
-// TODO: implement peak detector
-/*
- static void peakDetector() {
- return;
- } */
-
-// TODO put this function in separate file. Make it return a std::string instead of modifying body
-// TODO create a subclass of Reader for this and related work
+// TODO Make it return a std::string instead of modifying body --> is this still relevant?
// FIXME: as can be seen when printing the values, the outlier timestamps typically occur
// in the first histogram 35 to 38 indices from the end (most often 35).
-// TODO: build histogram buckets earlier and discard timestamps to save memory
// TODO consider changing all ints to uint32_t or uint64_t
-void PerformanceAnalysis::reportPerformance(String8 *body,
- const std::deque<std::pair
- <int, short_histogram>> &shortHists,
- int maxHeight) {
- if (shortHists.size() < 1) {
+void PerformanceAnalysis::reportPerformance(String8 *body, int maxHeight) {
+ if (mRecentHists.size() < 1) {
+ ALOGD("reportPerformance: mRecentHists is empty");
return;
}
- // this is temporary code, which only prints out one histogram
- // of all data stored in buffer. The data is not erased, only overwritten.
+ ALOGD("reportPerformance: hists size %d", static_cast<int>(mRecentHists.size()));
// TODO: more elaborate data analysis
std::map<int, int> buckets;
- for (const auto &shortHist: shortHists) {
+ for (const auto &shortHist: mRecentHists) {
for (const auto &countPair : shortHist.second) {
buckets[countPair.first] += countPair.second;
}
@@ -222,14 +323,4 @@
return;
}
-bool PerformanceAnalysis::isFindGlitch() const
-{
- return findGlitch;
-}
-
-void PerformanceAnalysis::setFindGlitch(bool s)
-{
- findGlitch = s;
-}
-
} // namespace android
diff --git a/media/libnbaio/include/media/nbaio/NBLog.h b/media/libnbaio/include/media/nbaio/NBLog.h
index 6b08e94..0b5e24d 100644
--- a/media/libnbaio/include/media/nbaio/NBLog.h
+++ b/media/libnbaio/include/media/nbaio/NBLog.h
@@ -406,6 +406,7 @@
public:
// A snapshot of a readers buffer
+ // This is raw data. No analysis has been done on it
class Snapshot {
public:
Snapshot() : mData(NULL), mLost(0) {}
@@ -445,6 +446,7 @@
// get snapshot of readers fifo buffer, effectively consuming the buffer
std::unique_ptr<Snapshot> getSnapshot();
// dump a particular snapshot of the reader
+ // TODO: move dump to PerformanceAnalysis. Model/view/controller design
void dump(int fd, size_t indent, Snapshot & snap);
// dump the current content of the reader's buffer (call getSnapshot() and previous dump())
void dump(int fd, size_t indent = 0);
@@ -452,9 +454,6 @@
private:
- // TODO: decide whether these belong in NBLog::Reader or in PerformanceAnalysis
- static const int kShortHistSize = 50; // number of samples in a short-term histogram
- static const int kRecentHistsCapacity = 100; // number of short-term histograms stored in memory
static const std::set<Event> startingTypes;
static const std::set<Event> endingTypes;
/*const*/ Shared* const mShared; // raw pointer to shared memory, actually const but not
@@ -467,23 +466,6 @@
audio_utils_fifo_reader * const mFifoReader; // used to read from FIFO,
// non-NULL unless constructor fails
- // stores a short-term histogram of size determined by kShortHistSize
- // TODO: unsigned, unsigned
- using short_histogram = std::map<int, int>;
-
- // each pair contains a sequence of timestamps (one histogram's worth)
- // pair's log_hash_t is the hash of the source code location where the timestamp was taken
- // pair's int points to the Reader that originated the entry
- std::map<std::pair<log_hash_t, int>, std::vector<int64_t>> mHists;
-
- // mHistsCopy stores timestamp vectors whose key is the reader thread index.
- // TODO remove old mHists after changing the code
- std::map<int, std::vector<int64_t>> mTimeStampSeries;
-
- // stores fixed-size short buffer period histograms with hash and thread data
- // TODO: Turn it into a circular buffer for better data flow
- std::deque<std::pair<int, short_histogram>> mRecentHists;
-
// TODO: it might be clearer, instead of a direct map from source location to vector of
// timestamps, if we instead first mapped from source location to an object that
// represented that location. And one_of its fields would be a vector of timestamps.
diff --git a/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h b/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h
index 32ae38b..89699bf 100644
--- a/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h
+++ b/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h
@@ -22,79 +22,119 @@
#include <map>
#include <deque>
#include <vector>
+#include "NBLog.h"
namespace android {
class String8;
class PerformanceAnalysis {
-
+ // This class stores and analyzes audio processing wakeup timestamps from NBLog
+ // FIXME: currently, all performance data is stored in deques. Need to add a mutex.
+ // FIXME: continue this way until analysis is done in a separate thread. Then, use
+ // the fifo writer utilities.
public:
-PerformanceAnalysis();
+ PerformanceAnalysis();
-// stores a short-term histogram of size determined by kShortHistSize
-// TODO: unsigned, unsigned
-// CHECK: is there a better way to use short_histogram than to write 'using'
-// both in this header file and in NBLog.h?
-using short_histogram = std::map<int, int>;
+ // FIXME: decide whether to use 64 or 32 bits
+ typedef uint64_t log_hash_t;
-// returns a vector of pairs <outlier timestamp, time elapsed since previous outlier
-// called by NBLog::Reader::dump before data is converted into histogram
-// TODO: currently, the elapsed time
-// The resolution is only as good as the ms duration of one shortHist
-void storeOutlierData(int author, const std::vector<int64_t> ×tamps);
+ // stores a short-term histogram of size determined by kShortHistSize
+ // key: observed buffer period. value: count
+ // TODO: unsigned, unsigned
+ // TODO: change this name to histogram
+ using short_histogram = std::map<int, int>;
-// TODO: delete this. temp for testing
-void testFunction();
+ using outlierInterval = uint64_t;
+ // int64_t timestamps are converted to uint64_t in PerformanceAnalysis::storeOutlierData,
+ // and all further analysis functions use uint64_t.
+ using timestamp = uint64_t;
+ using timestamp_raw = int64_t;
-// Given a series, looks for changes in distribution (peaks)
-// Returns a 'signal' array of the same length as the series, where each
-// value is mapped to -1, 0, or 1 based on whether a negative or positive peak
-// was detected, or no significant change occurred.
-// The function sets the mean to the starting value and sigma to 0, and updates
-// them as long as no peak is detected. When a value is more than 'threshold'
-// standard deviations from the mean, a peak is detected and the mean and sigma
-// are set to the peak value and 0.
-// static void peakDetector();
+ // Writes wakeup timestamp entry to log and runs analysis
+ // author is the thread ID
+ // TODO: check. if the thread has multiple histograms, is author info correct
+ // FIXME: remove author from arglist. Want to call these function separately on
+ // each thread’s data.
+ // FIXME: decide whether to store the hash (source file location) instead
+ // FIXME: If thread has multiple histograms, check that code works and correct
+ // author is stored (test with multiple threads). Need to check that the current
+ // code is not receiving data from multiple threads. This could cause odd values.
+ void logTsEntry(int author, timestamp_raw ts);
-// input: series of short histograms. output: prints an analysis of the
-// data to the console
-// TODO: change this so that it writes the analysis to the long-term
-// circular buffer and prints an analyses both for the short and long-term
-void reportPerformance(String8 *body,
- const std::deque<std::pair
- <int, short_histogram>> &shortHists,
- int maxHeight = 10);
+ // FIXME: make peakdetector and storeOutlierData a single function
+ // Input: mOutlierData. Looks at time elapsed between outliers
+ // finds significant changes in the distribution
+ // writes timestamps of significant changes to mPeakTimestamps
+ void detectPeaks();
-// if findGlitch is true, log warning when buffer periods caused glitch
-// TODO adapt this to the analysis in reportPerformance instead of logging
-void alertIfGlitch(const std::vector<int64_t> &samples);
-bool isFindGlitch() const;
-void setFindGlitch(bool s);
+ // runs analysis on timestamp series before it is converted to a histogram
+ // finds outliers
+ // writes to mOutlierData <time elapsed since previous outlier, outlier timestamp>
+ void storeOutlierData(const std::vector<timestamp_raw> ×tamps);
-~PerformanceAnalysis() {}
+ // input: series of short histograms. Generates a string of analysis of the buffer periods
+ // TODO: WIP write more detailed analysis
+ // FIXME: move this data visualization to a separate class. Model/view/controller
+ void reportPerformance(String8 *body, int maxHeight = 10);
+
+ // TODO: delete this. temp for testing
+ void testFunction();
+
+ // This function used to detect glitches in a time series
+ // TODO incorporate this into the analysis (currently unused)
+ void alertIfGlitch(const std::vector<timestamp_raw> &samples);
+
+ ~PerformanceAnalysis() {}
private:
-// stores outlier analysis
-std::vector<std::pair<uint64_t, uint64_t>> mOutlierData;
+ // stores outlier analysis: <elapsed time between outliers in ms, outlier timestamp>
+ std::deque<std::pair<outlierInterval, timestamp>> mOutlierData;
-// stores long-term audio performance data
-// TODO: Turn it into a circular buffer
-std::deque<std::pair<int, int>> mPerformanceAnalysis;
+ // stores each timestamp at which a peak was detected
+ // a peak is a moment at which the average outlier interval changed significantly
+ std::deque<timestamp> mPeakTimestamps;
-// alert if a local buffer period sequence caused an audio glitch
-bool findGlitch;
-//TODO: measure these from the data (e.g., mode) as they may change.
-//const int kGlitchThreshMs = 7;
-// const int kMsPerSec = 1000;
-const int kNumBuff = 3; // number of buffers considered in local history
-const int kPeriodMs = 4; // current period length is ideally 4 ms
-const int kOutlierMs = 7; // values greater or equal to this cause glitches every time
-// DAC processing time for 4 ms buffer
-static constexpr double kRatio = 0.75; // estimate of CPU time as ratio of period length
-int kPeriodMsCPU; //compute based on kPeriodLen and kRatio
+ // FIFO of small histograms
+ // stores fixed-size short buffer period histograms with hash and thread data
+ // TODO: Turn it into a circular buffer for better data flow
+ std::deque<std::pair<int, short_histogram>> mRecentHists;
+
+ // map from author to vector of timestamps, collected from NBLog
+ // when a vector reaches its maximum size, analysis is run and the data is deleted
+ std::map<int, std::vector<timestamp_raw>> mTimeStampSeries;
+
+ // TODO: measure these from the data (e.g., mode) as they may change.
+ // const int kGlitchThreshMs = 7;
+ // const int kMsPerSec = 1000;
+
+ // Parameters used when detecting outliers
+ // TODO: learn some of these from the data, delete unused ones
+ // FIXME: decide whether to make kPeriodMs static.
+ // The non-const values are (TODO: will be) learned from the data
+ static const int kNumBuff = 3; // number of buffers considered in local history
+ int kPeriodMs; // current period length is ideally 4 ms
+ static const int kOutlierMs = 7; // values greater or equal to this cause glitches
+ // DAC processing time for 4 ms buffer
+ static constexpr double kRatio = 0.75; // estimate of CPU time as ratio of period length
+ int kPeriodMsCPU; // compute based on kPeriodLen and kRatio
+
+ // Peak detection: number of standard deviations from mean considered a significant change
+ static const int kStddevThreshold = 5;
+
+ static const int kRecentHistsCapacity = 100; // number of short-term histograms stored in memory
+ static const int kShortHistSize = 50; // number of samples in a short-term histogram
+
+ // these variables are stored in-class to ensure continuity while analyzing the timestamp
+ // series one short sequence at a time: the variables are not re-initialized every time.
+ // FIXME: create inner class for these variables and decide which other ones to add to it
+ double mPeakDetectorMean = -1;
+ double mPeakDetectorSd = -1;
+ // variables for storeOutlierData
+ uint64_t mElapsed = 0;
+ int64_t mPrevNs = -1;
};
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 61a2b5f..6ed0d0e 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -972,6 +972,14 @@
}
if (handle != nullptr) {
+ ssize_t offset;
+ size_t size;
+ sp<IMemoryHeap> heap = frame->getMemory(&offset, &size);
+ if (heap->getHeapID() != mMemoryHeapBase->getHeapID()) {
+ ALOGE("%s: Mismatched heap ID, ignoring release (got %x, expected %x)",
+ __FUNCTION__, heap->getHeapID(), mMemoryHeapBase->getHeapID());
+ return;
+ }
uint32_t batchSize = 0;
{
Mutex::Autolock autoLock(mBatchLock);
diff --git a/services/audiopolicy/Android.mk b/services/audiopolicy/Android.mk
index ad340e5..65571f9 100644
--- a/services/audiopolicy/Android.mk
+++ b/services/audiopolicy/Android.mk
@@ -24,7 +24,8 @@
libhardware_legacy \
libserviceutility \
libaudiopolicymanager \
- libmedia_helper
+ libmedia_helper \
+ libeffectsconfig
LOCAL_STATIC_LIBRARIES := \
libaudiopolicycomponents
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPort.h b/services/audiopolicy/common/managerdefinitions/include/AudioPort.h
index ded2285..4f79ed2 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPort.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPort.h
@@ -71,7 +71,7 @@
virtual void toAudioPort(struct audio_port *port) const;
- virtual void importAudioPort(const sp<AudioPort>& port);
+ virtual void importAudioPort(const sp<AudioPort>& port, bool force = false);
void addAudioProfile(const sp<AudioProfile> &profile) { mProfiles.add(profile); }
diff --git a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
index 9a52d22..1a644d7 100644
--- a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
@@ -48,7 +48,7 @@
// AudioPort
virtual void attach(const sp<HwModule>& module);
virtual void toAudioPort(struct audio_port *port) const;
- virtual void importAudioPort(const sp<AudioPort>& port);
+ virtual void importAudioPort(const sp<AudioPort>& port, bool force = false);
audio_port_handle_t getId() const;
status_t dump(int fd, int spaces, int index, bool verbose = true) const;
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
index 6ed2cb7..fcf9070 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
@@ -128,7 +128,7 @@
port->num_gains = i;
}
-void AudioPort::importAudioPort(const sp<AudioPort>& port)
+void AudioPort::importAudioPort(const sp<AudioPort>& port, bool force __unused)
{
size_t indexToImport;
for (indexToImport = 0; indexToImport < port->mProfiles.size(); indexToImport++) {
diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
index f0e48b6..a2c1165 100644
--- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
@@ -263,7 +263,10 @@
strncpy(port->ext.device.address, mAddress.string(), AUDIO_DEVICE_MAX_ADDRESS_LEN);
}
-void DeviceDescriptor::importAudioPort(const sp<AudioPort>& port) {
+void DeviceDescriptor::importAudioPort(const sp<AudioPort>& port, bool force) {
+ if (!force && !port->hasDynamicAudioProfile()) {
+ return;
+ }
AudioPort::importAudioPort(port);
port->pickAudioProfile(mSamplingRate, mChannelMask, mFormat);
}
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 77b0182..3dcb22d 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -3724,7 +3724,7 @@
sp<DeviceDescriptor> devDesc = mAvailableInputDevices[index];
if (!devDesc->isAttached()) {
devDesc->attach(mHwModules[i]);
- devDesc->importAudioPort(inProfile);
+ devDesc->importAudioPort(inProfile, true);
}
}
}
@@ -4087,8 +4087,8 @@
continue;
}
- ALOGV("opening output for device %08x with params %s profile %p",
- device, address.string(), profile.get());
+ ALOGV("opening output for device %08x with params %s profile %p name %s",
+ device, address.string(), profile.get(), profile->getName().string());
desc = new SwAudioOutputDescriptor(profile, mpClientInterface);
desc->mDevice = device;
audio_config_t config = AUDIO_CONFIG_INITIALIZER;
@@ -4337,6 +4337,10 @@
config.channel_mask = desc->mChannelMask;
config.format = desc->mFormat;
audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
+
+ ALOGV("opening inputput for device %08x with params %s profile %p name %s",
+ desc->mDevice, address.string(), profile.get(), profile->getName().string());
+
status_t status = mpClientInterface->openInput(profile->getModuleHandle(),
&input,
&config,
diff --git a/services/audiopolicy/service/AudioPolicyEffects.cpp b/services/audiopolicy/service/AudioPolicyEffects.cpp
index 654465d..84b1073 100644
--- a/services/audiopolicy/service/AudioPolicyEffects.cpp
+++ b/services/audiopolicy/service/AudioPolicyEffects.cpp
@@ -20,8 +20,10 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <memory>
#include <cutils/misc.h>
#include <media/AudioEffect.h>
+#include <media/EffectsConfig.h>
#include <system/audio.h>
#include <system/audio_effects/audio_effects_conf.h>
#include <utils/Vector.h>
@@ -39,11 +41,17 @@
AudioPolicyEffects::AudioPolicyEffects()
{
- // load automatic audio effect modules
- if (access(AUDIO_EFFECT_VENDOR_CONFIG_FILE, R_OK) == 0) {
- loadAudioEffectConfig(AUDIO_EFFECT_VENDOR_CONFIG_FILE);
- } else if (access(AUDIO_EFFECT_DEFAULT_CONFIG_FILE, R_OK) == 0) {
- loadAudioEffectConfig(AUDIO_EFFECT_DEFAULT_CONFIG_FILE);
+ status_t loadResult = loadAudioEffectXmlConfig();
+ if (loadResult < 0) {
+ ALOGW("Failed to load XML effect configuration, fallback to .conf");
+ // load automatic audio effect modules
+ if (access(AUDIO_EFFECT_VENDOR_CONFIG_FILE, R_OK) == 0) {
+ loadAudioEffectConfig(AUDIO_EFFECT_VENDOR_CONFIG_FILE);
+ } else if (access(AUDIO_EFFECT_DEFAULT_CONFIG_FILE, R_OK) == 0) {
+ loadAudioEffectConfig(AUDIO_EFFECT_DEFAULT_CONFIG_FILE);
+ }
+ } else if (loadResult > 0) {
+ ALOGE("Effect config is partially invalid, skipped %d elements", loadResult);
}
}
@@ -685,6 +693,28 @@
return NO_ERROR;
}
+status_t AudioPolicyEffects::loadAudioEffectXmlConfig() {
+ auto result = effectsConfig::parse();
+ if (result.parsedConfig == nullptr) {
+ return -ENOENT;
+ }
+
+ auto loadProcessingChain = [](auto& processingChain, auto& streams) {
+ for (auto& stream : processingChain) {
+ auto effectDescs = std::make_unique<EffectDescVector>();
+ for (auto& effect : stream.effects) {
+ effectDescs->mEffects.add(
+ new EffectDesc{effect.get().name.c_str(), effect.get().uuid});
+ }
+ streams.add(stream.type, effectDescs.release());
+ }
+ };
+ loadProcessingChain(result.parsedConfig->preprocess, mInputSources);
+ loadProcessingChain(result.parsedConfig->postprocess, mOutputStreams);
+ // Casting from ssize_t to status_t is probably safe, there should not be more than 2^31 errors
+ return result.nbSkippedElement;
+}
+
status_t AudioPolicyEffects::loadAudioEffectConfig(const char *path)
{
cnode *root;
diff --git a/services/audiopolicy/service/AudioPolicyEffects.h b/services/audiopolicy/service/AudioPolicyEffects.h
index 0c74d87..59d5d14 100644
--- a/services/audiopolicy/service/AudioPolicyEffects.h
+++ b/services/audiopolicy/service/AudioPolicyEffects.h
@@ -155,7 +155,8 @@
audio_stream_type_t streamNameToEnum(const char *name);
// Parse audio_effects.conf
- status_t loadAudioEffectConfig(const char *path);
+ status_t loadAudioEffectConfig(const char *path); // TODO: add legacy in the name
+ status_t loadAudioEffectXmlConfig(); // TODO: remove "Xml" in the name
// Load all effects descriptors in configuration file
status_t loadEffects(cnode *root, Vector <EffectDesc *>& effects);