1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #ifndef LOG_TAG
16 #define LOG_TAG "AudioConverterParser"
17 #endif
18
19 #include "audio_converter_parser.h"
20 #include <libxml/tree.h>
21 #ifdef USE_CONFIG_POLICY
22 #endif
23
24 #include "media_monitor_manager.h"
25
26 namespace OHOS {
27 namespace AudioStandard {
28
29 #ifdef USE_CONFIG_POLICY
30 static constexpr char AUDIO_CONVERTER_CONFIG_FILE[] = "/etc/audio/audio_converter_config.xml";
31 #else
32 static constexpr char AUDIO_CONVERTER_CONFIG_FILE[] = "/system/etc/audio/audio_converter_config.xml";
33 #endif
34
35 static constexpr int32_t FILE_CONTENT_ERROR = -2;
36 static constexpr int32_t FILE_PARSE_ERROR = -3;
37
38 enum XML_ERROR {
39 XML_PARSE_RECOVER = 1 << 0, // recover on errors
40 XML_PARSE_NOERROR = 1 << 5, // suppress error reports
41 XML_PARSE_NOWARNING = 1 << 6, // suppress warning reports
42 XML_PARSE_PEDANTIC = 1 << 7 // pedantic error reporting
43 };
44
45 static std::map<std::string, AudioChannelLayout> str2layout = {
46 {"CH_LAYOUT_UNKNOWN", CH_LAYOUT_UNKNOWN},
47 {"CH_LAYOUT_MONO", CH_LAYOUT_MONO},
48 {"CH_LAYOUT_STEREO", CH_LAYOUT_STEREO},
49 {"CH_LAYOUT_STEREO_DOWNMIX", CH_LAYOUT_STEREO_DOWNMIX},
50 {"CH_LAYOUT_2POINT1", CH_LAYOUT_2POINT1},
51 {"CH_LAYOUT_3POINT0", CH_LAYOUT_3POINT0},
52 {"CH_LAYOUT_SURROUND", CH_LAYOUT_SURROUND},
53 {"CH_LAYOUT_3POINT1", CH_LAYOUT_3POINT1},
54 {"CH_LAYOUT_4POINT0", CH_LAYOUT_4POINT0},
55 {"CH_LAYOUT_QUAD_SIDE", CH_LAYOUT_QUAD_SIDE},
56 {"CH_LAYOUT_QUAD", CH_LAYOUT_QUAD},
57 {"CH_LAYOUT_2POINT0POINT2", CH_LAYOUT_2POINT0POINT2},
58 {"CH_LAYOUT_4POINT1", CH_LAYOUT_4POINT1},
59 {"CH_LAYOUT_5POINT0", CH_LAYOUT_5POINT0},
60 {"CH_LAYOUT_5POINT0_BACK", CH_LAYOUT_5POINT0_BACK},
61 {"CH_LAYOUT_2POINT1POINT2", CH_LAYOUT_2POINT1POINT2},
62 {"CH_LAYOUT_3POINT0POINT2", CH_LAYOUT_3POINT0POINT2},
63 {"CH_LAYOUT_5POINT1", CH_LAYOUT_5POINT1},
64 {"CH_LAYOUT_5POINT1_BACK", CH_LAYOUT_5POINT1_BACK},
65 {"CH_LAYOUT_6POINT0", CH_LAYOUT_6POINT0},
66 {"CH_LAYOUT_HEXAGONAL", CH_LAYOUT_HEXAGONAL},
67 {"CH_LAYOUT_3POINT1POINT2", CH_LAYOUT_3POINT1POINT2},
68 {"CH_LAYOUT_6POINT0_FRONT", CH_LAYOUT_6POINT0_FRONT},
69 {"CH_LAYOUT_6POINT1", CH_LAYOUT_6POINT1},
70 {"CH_LAYOUT_6POINT1_BACK", CH_LAYOUT_6POINT1_BACK},
71 {"CH_LAYOUT_6POINT1_FRONT", CH_LAYOUT_6POINT1_FRONT},
72 {"CH_LAYOUT_7POINT0", CH_LAYOUT_7POINT0},
73 {"CH_LAYOUT_7POINT0_FRONT", CH_LAYOUT_7POINT0_FRONT},
74 {"CH_LAYOUT_7POINT1", CH_LAYOUT_7POINT1},
75 {"CH_LAYOUT_OCTAGONAL", CH_LAYOUT_OCTAGONAL},
76 {"CH_LAYOUT_5POINT1POINT2", CH_LAYOUT_5POINT1POINT2},
77 {"CH_LAYOUT_7POINT1_WIDE", CH_LAYOUT_7POINT1_WIDE},
78 {"CH_LAYOUT_7POINT1_WIDE_BACK", CH_LAYOUT_7POINT1_WIDE_BACK},
79 {"CH_LAYOUT_5POINT1POINT4", CH_LAYOUT_5POINT1POINT4},
80 {"CH_LAYOUT_7POINT1POINT2", CH_LAYOUT_7POINT1POINT2},
81 {"CH_LAYOUT_7POINT1POINT4", CH_LAYOUT_7POINT1POINT4},
82 {"CH_LAYOUT_10POINT2", CH_LAYOUT_10POINT2},
83 {"CH_LAYOUT_9POINT1POINT4", CH_LAYOUT_9POINT1POINT4},
84 {"CH_LAYOUT_9POINT1POINT6", CH_LAYOUT_9POINT1POINT6},
85 {"CH_LAYOUT_HEXADECAGONAL", CH_LAYOUT_HEXADECAGONAL},
86 {"CH_LAYOUT_AMB_ORDER1_ACN_N3D", CH_LAYOUT_HOA_ORDER1_ACN_N3D},
87 {"CH_LAYOUT_AMB_ORDER1_ACN_SN3D", CH_LAYOUT_HOA_ORDER1_ACN_SN3D},
88 {"CH_LAYOUT_AMB_ORDER1_FUMA", CH_LAYOUT_HOA_ORDER1_FUMA},
89 {"CH_LAYOUT_AMB_ORDER2_ACN_N3D", CH_LAYOUT_HOA_ORDER2_ACN_N3D},
90 {"CH_LAYOUT_AMB_ORDER2_ACN_SN3D", CH_LAYOUT_HOA_ORDER2_ACN_SN3D},
91 {"CH_LAYOUT_AMB_ORDER2_FUMA", CH_LAYOUT_HOA_ORDER2_FUMA},
92 {"CH_LAYOUT_AMB_ORDER3_ACN_N3D", CH_LAYOUT_HOA_ORDER3_ACN_N3D},
93 {"CH_LAYOUT_AMB_ORDER3_ACN_SN3D", CH_LAYOUT_HOA_ORDER3_ACN_SN3D},
94 {"CH_LAYOUT_AMB_ORDER3_FUMA", CH_LAYOUT_HOA_ORDER3_FUMA},
95 };
96
WriteConverterConfigError()97 static void WriteConverterConfigError()
98 {
99 std::shared_ptr<Media::MediaMonitor::EventBean> bean = std::make_shared<Media::MediaMonitor::EventBean>(
100 Media::MediaMonitor::AUDIO, Media::MediaMonitor::LOAD_CONFIG_ERROR,
101 Media::MediaMonitor::FAULT_EVENT);
102 bean->Add("CATEGORY", Media::MediaMonitor::AUDIO_CONVERTER_CONFIG);
103 Media::MediaMonitor::MediaMonitorManager::GetInstance().WriteLogMsg(bean);
104 }
105
ParseEffectConfigFile(xmlDoc * & doc)106 static void ParseEffectConfigFile(xmlDoc* &doc)
107 {
108 AUDIO_INFO_LOG("use default audio effect config file path: %{public}s", AUDIO_CONVERTER_CONFIG_FILE);
109 doc = xmlReadFile(AUDIO_CONVERTER_CONFIG_FILE, nullptr, XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
110 }
111
AudioConverterParser()112 AudioConverterParser::AudioConverterParser()
113 {
114 AUDIO_INFO_LOG("AudioConverterParser created");
115 }
116
LoadConfigCheck(xmlDoc * doc,xmlNode * currNode)117 static int32_t LoadConfigCheck(xmlDoc *doc, xmlNode *currNode)
118 {
119 CHECK_AND_RETURN_RET_LOG(currNode != nullptr, FILE_PARSE_ERROR, "error: could not parse file %{public}s",
120 AUDIO_CONVERTER_CONFIG_FILE);
121 bool ret = xmlStrcmp(currNode->name, reinterpret_cast<const xmlChar *>("audio_converter_conf"));
122 CHECK_AND_RETURN_RET_LOG(!ret, FILE_CONTENT_ERROR, "Missing tag - audio_converter_conf: %{public}s",
123 AUDIO_CONVERTER_CONFIG_FILE);
124 CHECK_AND_RETURN_RET_LOG(currNode->xmlChildrenNode != nullptr, FILE_CONTENT_ERROR,
125 "Missing node - audio_converter_conf: %s", AUDIO_CONVERTER_CONFIG_FILE);
126
127 return 0;
128 }
129
LoadConfigLibrary(ConverterConfig & result,xmlNode * currNode)130 static void LoadConfigLibrary(ConverterConfig &result, xmlNode *currNode)
131 {
132 if (!xmlHasProp(currNode, reinterpret_cast<const xmlChar *>("name"))) {
133 AUDIO_WARNING_LOG("missing information: library has no name attribute");
134 } else if (!xmlHasProp(currNode, reinterpret_cast<const xmlChar *>("path"))) {
135 AUDIO_WARNING_LOG("missing information: library has no path attribute");
136 } else {
137 std::string libName = reinterpret_cast<char *>(xmlGetProp(currNode, reinterpret_cast<const xmlChar *>("name")));
138 std::string libPath = reinterpret_cast<char *>(xmlGetProp(currNode, reinterpret_cast<const xmlChar *>("path")));
139 result.library = {libName, libPath};
140 }
141 }
142
LoadConfigChannelLayout(ConverterConfig & result,xmlNode * currNode)143 static void LoadConfigChannelLayout(ConverterConfig &result, xmlNode *currNode)
144 {
145 if (!xmlHasProp(currNode, reinterpret_cast<const xmlChar *>("out_channel_layout"))) {
146 AUDIO_ERR_LOG("missing information: config has no out_channel_layout attribute, set to default STEREO");
147 result.outChannelLayout = CH_LAYOUT_STEREO;
148 } else {
149 std::string strChannelLayout =
150 reinterpret_cast<char *>(xmlGetProp(currNode, reinterpret_cast<const xmlChar *>("out_channel_layout")));
151 if (str2layout.count(strChannelLayout) == 0) {
152 AUDIO_ERR_LOG("unsupported format: invalid channel layout, set to STEREO");
153 result.outChannelLayout = CH_LAYOUT_STEREO;
154 } else {
155 result.outChannelLayout = str2layout[strChannelLayout];
156 AUDIO_INFO_LOG("AudioVivid MCR output format is %{public}s", strChannelLayout.c_str());
157 }
158 }
159 }
160
LoadConfigVersion(ConverterConfig & result,xmlNode * currNode)161 static void LoadConfigVersion(ConverterConfig &result, xmlNode *currNode)
162 {
163 bool ret = xmlHasProp(currNode, reinterpret_cast<const xmlChar *>("version"));
164 CHECK_AND_RETURN_LOG(ret, "missing information: audio_converter_conf node has no version attribute");
165
166 result.version = reinterpret_cast<char *>(xmlGetProp(currNode, reinterpret_cast<const xmlChar *>("version")));
167 }
168
GetInstance()169 AudioConverterParser &AudioConverterParser::GetInstance()
170 {
171 static AudioConverterParser instance;
172 return instance;
173 }
174
LoadConfig()175 ConverterConfig AudioConverterParser::LoadConfig()
176 {
177 std::lock_guard<std::mutex> lock(loadConfigMutex_);
178 int32_t ret = 0;
179 AUDIO_INFO_LOG("AudioConverterParser::LoadConfig");
180 CHECK_AND_RETURN_RET(cfg_ == nullptr, *cfg_);
181 xmlDoc *doc = nullptr;
182 xmlNode *rootElement = nullptr;
183 cfg_ = std::make_unique<ConverterConfig>();
184 ConverterConfig &result = *cfg_;
185
186 ParseEffectConfigFile(doc);
187 if (doc == nullptr) {
188 WriteConverterConfigError();
189 }
190 CHECK_AND_RETURN_RET_LOG(doc != nullptr, result, "error: could not parse file %{public}s",
191 AUDIO_CONVERTER_CONFIG_FILE);
192
193 rootElement = xmlDocGetRootElement(doc);
194 xmlNode *currNode = rootElement;
195
196 if ((ret = LoadConfigCheck(doc, currNode)) != 0) {
197 xmlFreeDoc(doc);
198 return result;
199 }
200
201 LoadConfigVersion(result, currNode);
202 currNode = currNode->xmlChildrenNode;
203
204 while (currNode != nullptr) {
205 if (currNode->type != XML_ELEMENT_NODE) {
206 currNode = currNode->next;
207 continue;
208 }
209
210 if (!xmlStrcmp(currNode->name, reinterpret_cast<const xmlChar *>("library"))) {
211 LoadConfigLibrary(result, currNode);
212 } else if (!xmlStrcmp(currNode->name, reinterpret_cast<const xmlChar *>("converter_conf"))) {
213 LoadConfigChannelLayout(result, currNode);
214 }
215
216 currNode = currNode->next;
217 }
218 xmlFreeDoc(doc);
219 return result;
220 }
221 } // namespace AudioStandard
222 } // namespace OHOS