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