1# AI 2 3## Introduction 4 5The AI subsystem is the part of OpenHarmony that provides native distributed AI capabilities. At the heart of the subsystem is a unified AI engine framework, which implements quick integration of AI algorithm plug-ins. The framework consists of the plug-in management, module management, and communication management modules, fulfilling lifecycle management and on-demand deployment of AI algorithms. Under this framework, AI algorithm APIs will be standardized to facilitate distributed calling of AI capabilities. In addition, unified inference APIs will be provided to adapt to different inference framework hierarchies. 6 7**Figure 1** AI engine framework<a name="fig17296164711526"></a> 8![](figures/ai-engine-framework.png "ai-engine-framework") 9 10## Directory Structure 11 12``` 13/foundation/ai/engine # Home directory of the AI subsystem 14├── interfaces 15│ └── kits # External APIs of the AI subsystem 16└── services 17│ ├── client # Client module of the AI subsystem 18│ │ ├── client_executor # Executor of the client module 19│ │ └── communication_adapter # Communication adaptation layer for the client module, with extension supported 20│ ├── common # Common tools and protocol modules of the AI subsystem 21│ │ ├── platform 22│ │ ├── protocol 23│ │ └── utils 24│ └── server # Server module of the AI subsystem 25│ │ ├── communication_adapter # Communication adaptation layer for the server module, with extension supported 26│ │ ├── plugin 27│ │ ├── asr 28│ │ └── keyword_spotting # ASR algorithm plug-in reference: keyword spotting 29│ │ └── cv 30│ │ └── image_classification # CV algorithm plug-in reference: image classification 31│ │ ├── plugin_manager 32│ │ └── server_executor # Executor of the server module 33``` 34 35## Constraints 36 37* **Programming language**: C/C++ 38 39* **Operating system**: OpenHarmony mini- and small-system 40 41* **Others**: The System Ability Manager \(Samgr\) has been started and is running properly. 42 43## Usage 44 451. Compile the AI subsystem. 46 47 The source code for lightweight AI framework is available at **//foundation/ai/engine/services**. 48 49 The compilation procedure is as follows: 50 51 1. Set the compilation path. 52 53 ``` 54 hb set -root dir (root dir is the root directory of the project code.) 55 ``` 56 57 2. Specify the product for compilation. \(After the product list is displayed using the following command, move to the desired product with arrow keys and press **Enter**.\) 58 59 ``` 60 hb set -p 61 ``` 62 63 3. Start compilation. 64 65 ``` 66 hb build -f (Use this command if you want to compile the entire repository.) 67 hb build ai_engine (Use this command if you want to compile only the ai_engine module.) 68 ``` 69 70 >**Note**: For details about the hb configuration, see the readme of the build\_lite subsystem. 71 722. Develop the plug-in, with keyword spotting as an example. 73 74 Directory: //foundation/ai/engine/services/server/plugin/asr/keyword\_spotting 75 76 >**Note**: The plug-in must implement the **IPlugin** and **IPluginCallback** APIs provided by the server. 77 78 ``` 79 #include "plugin/i_plugin.h 80 class KWSPlugin : public IPlugin { // Inherits the public base class of the IPlugin API for Keywords Spotting Plugin (KWSPlugin). 81 KWSPlugin(); 82 ~KWSPlugin(); 83 84 const long long GetVersion() const override; 85 const char* GetName() const override; 86 const char* GetInferMode() const override; 87 88 int32_t Prepare(long long transactionId, const DataInfo &amp;inputInfo, DataInfo &amp;outputInfo) override; 89 int32_t SetOption(int optionType, const DataInfo &amp;inputInfo) override; 90 int32_t GetOption(int optionType, const DataInfo &amp;inputInfo, DataInfo &amp;outputInfo) override; 91 int32_t SyncProcess(IRequest *request, IResponse *&amp;response) override; 92 int32_t AsyncProcess(IRequest *request, IPluginCallback*callback) override; 93 int32_t Release(bool isFullUnload, long long transactionId, const DataInfo &amp;inputInfo) override; 94 . 95 . 96 . 97 }; 98 ``` 99 100 >**Note**: Depending on the algorithm in use, you only need to implement the **SyncProcess** or **AsyncProcess** API. Use an empty function as a placeholder for the other API. In this example, the KWS plug-in uses the synchronous algorithm. Therefore, you need to implement **SyncProcess** API and use an empty function as a placeholder for the **SyncProcess** API. 101 102 ``` 103 #include "aie_log.h" 104 #include "aie_retcode_inner.h" 105 . 106 . 107 . 108 109 const long long KWSPlugin::GetVersion() const 110 { 111 return ALGOTYPE_VERSION_KWS; 112 } 113 114 const char *KWSPlugin::GetName() const 115 { 116 return ALGORITHM_NAME_KWS.c_str(); 117 } 118 119 const char *KWSPlugin::GetInferMode() const 120 { 121 return DEFAULT_INFER_MODE.c_str(); 122 } 123 . 124 . 125 . 126 int32_t KWSPlugin::AsyncProcess(IRequest *request, IPluginCallback *callback) 127 { 128 return RETCODE_SUCCESS; 129 } 130 ``` 131 1323. Develop the SDK, with keyword spotting as an example. 133 134 Directory: //foundation/ai/engine/services/client/algorithm\_sdk/asr/keyword\_spotting 135 136 Keyword spotting SDK: 137 138 ``` 139 class KWSSdk { 140 public: 141 KWSSdk(); 142 virtual ~KWSSdk(); 143 144 /** 145 * @brief Create a new session with KWS Plugin 146 * 147 * @return Returns KWS_RETCODE_SUCCESS(0) if the operation is successful, 148 * returns a non-zero value otherwise. 149 */ 150 int32_t Create(); 151 152 /** 153 * @brief Synchronously execute keyword spotting once 154 * 155 * @param audioInput pcm data. 156 * @return Returns KWS_RETCODE_SUCCESS(0) if the operation is successful, 157 * returns a non-zero value otherwise. 158 */ 159 int32_t SyncExecute(const Array<int16_t> &audioInput); 160 161 /** 162 * @brief Asynchronously execute keyword spotting once 163 * 164 * @param audioInput pcm data. 165 * @return Returns KWS_RETCODE_SUCCESS(0) if the operation is successful, 166 * returns a non-zero value otherwise. 167 */ 168 int32_t AsyncExecute(const Array<int16_t> &audioInput); 169 170 /** 171 * @brief Set callback 172 * 173 * @param callback Callback function that will be called during the process. 174 * @return Returns KWS_RETCODE_SUCCESS(0) if the operation is successful, 175 * returns a non-zero value otherwise. 176 */ 177 int32_t SetCallback(const std::shared_ptr<KWSCallback> &callback); 178 179 /** 180 * @brief Destroy the created session with KWS Plugin 181 * 182 * @return Returns KWS_RETCODE_SUCCESS(0) if the operation is successful, 183 * returns a non-zero value otherwise. 184 */ 185 int32_t Destroy(); 186 } 187 ``` 188 189 >**Note**: The sequence for the SDK to call client APIs of the AI engine is as follows: AieClientInit -\> AieClientPrepare -\> AieClientSyncProcess/AieClientAsyncProcess -\> AieClientRelease -\> AieClientDestroy. An exception will be thrown if the call sequence is violated. In addition, all these APIs must be called. Otherwise, a memory leakage may occur. 190 191 ``` 192 int32_t KWSSdk::KWSSdkImpl::Create() 193 { 194 if (kwsHandle_ != INVALID_KWS_HANDLE) { 195 HILOGE("[KWSSdkImpl]The SDK has been created"); 196 return KWS_RETCODE_FAILURE; 197 } 198 if (InitComponents() != RETCODE_SUCCESS) { 199 HILOGE("[KWSSdkImpl]Fail to init sdk components"); 200 return KWS_RETCODE_FAILURE; 201 } 202 int32_t retCode = AieClientInit(configInfo_, clientInfo_, algorithmInfo_, nullptr); 203 if (retCode != RETCODE_SUCCESS) { 204 HILOGE("[KWSSdkImpl]AieClientInit failed. Error code[%d]", retCode); 205 return KWS_RETCODE_FAILURE; 206 } 207 if (clientInfo_.clientId == INVALID_CLIENT_ID) { 208 HILOGE("[KWSSdkImpl]Fail to allocate client id"); 209 return KWS_RETCODE_FAILURE; 210 } 211 DataInfo inputInfo = { 212 .data = nullptr, 213 .length = 0, 214 }; 215 DataInfo outputInfo = { 216 .data = nullptr, 217 .length = 0, 218 }; 219 retCode = AieClientPrepare(clientInfo_, algorithmInfo_, inputInfo, outputInfo, nullptr); 220 if (retCode != RETCODE_SUCCESS) { 221 HILOGE("[KWSSdkImpl]AieclientPrepare failed. Error code[%d]", retCode); 222 return KWS_RETCODE_FAILURE; 223 } 224 if (outputInfo.data == nullptr || outputInfo.length <= 0) { 225 HILOGE("[KWSSdkImpl]The data or length of output info is invalid"); 226 return KWS_RETCODE_FAILURE; 227 } 228 MallocPointerGuard<unsigned char> pointerGuard(outputInfo.data); 229 retCode = PluginHelper::UnSerializeHandle(outputInfo, kwsHandle_); 230 if (retCode != RETCODE_SUCCESS) { 231 HILOGE("[KWSSdkImpl]Get handle from inputInfo failed"); 232 return KWS_RETCODE_FAILURE; 233 } 234 return KWS_RETCODE_SUCCESS; 235 } 236 237 int32_t KWSSdk::KWSSdkImpl::SyncExecute(const Array<uint16_t> &audioInput) 238 { 239 intptr_t newHandle = 0; 240 Array<int32_t> kwsResult = { 241 .data = nullptr, 242 .size = 0 243 }; 244 DataInfo inputInfo = { 245 .data = nullptr, 246 .length = 0 247 }; 248 DataInfo outputInfo = { 249 .data = nullptr, 250 .length = 0 251 }; 252 int32_t retCode = PluginHelper::SerializeInputData(kwsHandle_, audioInput, inputInfo); 253 if (retCode != RETCODE_SUCCESS) { 254 HILOGE("[KWSSdkImpl]Fail to serialize input data"); 255 callback_->OnError(KWS_RETCODE_SERIALIZATION_ERROR); 256 return RETCODE_FAILURE; 257 } 258 retCode = AieClientSyncProcess(clientInfo_, algorithmInfo_, inputInfo, outputInfo); 259 if (retCode != RETCODE_SUCCESS) { 260 HILOGE("[KWSSdkImpl]AieClientSyncProcess failed. Error code[%d]", retCode); 261 callback_->OnError(KWS_RETCODE_PLUGIN_EXECUTION_ERROR); 262 return RETCODE_FAILURE; 263 } 264 if (outputInfo.data == nullptr || outputInfo.length <= 0) { 265 HILOGE("[KWSSdkImpl] The data or length of outputInfo is invalid. Error code[%d]", retCode); 266 callback_->OnError(KWS_RETCODE_NULL_PARAM); 267 return RETCODE_FAILURE; 268 } 269 MallocPointerGuard<unsigned char> pointerGuard(outputInfo.data); 270 retCode = PluginHelper::UnSerializeOutputData(outputInfo, newHandle, kwsResult); 271 if (retCode != RETCODE_SUCCESS) { 272 HILOGE("[KWSSdkImpl]UnSerializeOutputData failed. Error code[%d]", retCode); 273 callback_->OnError(KWS_RETCODE_UNSERIALIZATION_ERROR); 274 return retCode; 275 } 276 if (kwsHandle_ != newHandle) { 277 HILOGE("[KWSSdkImpl]The handle[%lld] of output data is not equal to the current handle[%lld]", 278 (long long)newHandle, (long long)kwsHandle_); 279 callback_->OnError(KWS_RETCODE_PLUGIN_SESSION_ERROR); 280 return RETCODE_FAILURE; 281 } 282 callback_->OnResult(kwsResult); 283 return RETCODE_SUCCESS; 284 } 285 286 int32_t KWSSdk::KWSSdkImpl::Destroy() 287 { 288 if (kwsHandle_ == INVALID_KWS_HANDLE) { 289 return KWS_RETCODE_SUCCESS; 290 } 291 DataInfo inputInfo = { 292 .data = nullptr, 293 .length = 0 294 }; 295 int32_t retCode = PluginHelper::SerializeHandle(kwsHandle_, inputInfo); 296 if (retCode != RETCODE_SUCCESS) { 297 HILOGE("[KWSSdkImpl]SerializeHandle failed. Error code[%d]", retCode); 298 return KWS_RETCODE_FAILURE; 299 } 300 retCode = AieClientRelease(clientInfo_, algorithmInfo_, inputInfo); 301 if (retCode != RETCODE_SUCCESS) { 302 HILOGE("[KWSSdkImpl]AieClientRelease failed. Error code[%d]", retCode); 303 return KWS_RETCODE_FAILURE; 304 } 305 retCode = AieClientDestroy(clientInfo_); 306 if (retCode != RETCODE_SUCCESS) { 307 HILOGE("[KWSSdkImpl]AieClientDestroy failed. Error code[%d]", retCode); 308 return KWS_RETCODE_FAILURE; 309 } 310 mfccProcessor_ = nullptr; 311 pcmIterator_ = nullptr; 312 callback_ = nullptr; 313 kwsHandle_ = INVALID_KWS_HANDLE; 314 return KWS_RETCODE_SUCCESS; 315 } 316 ``` 317 3184. Develop a sample application. 319 320 Call the **Create** API. 321 322 ``` 323 bool KwsManager::PreparedInference() 324 { 325 if (capturer_ == nullptr) { 326 printf("[KwsManager] only load plugin after AudioCapturer ready\n"); 327 return false; 328 } 329 if (plugin_ != nullptr) { 330 printf("[KwsManager] stop created InferencePlugin at first\n"); 331 StopInference(); 332 } 333 plugin_ = std::make_shared<KWSSdk>(); 334 if (plugin_ == nullptr) { 335 printf("[KwsManager] fail to create inferencePlugin\n"); 336 return false; 337 } 338 if (plugin_->Create() != SUCCESS) { 339 printf("[KwsManager] KWSSdk fail to create.\n"); 340 return false; 341 } 342 std::shared_ptr<KWSCallback> callback = std::make_shared<MyKwsCallback>(); 343 if (callback == nullptr) { 344 printf("[KwsManager] new Callback failed.\n"); 345 return false; 346 } 347 plugin_->SetCallback(callback); 348 return true; 349 } 350 ``` 351 352 Call the **SyncExecute** API. 353 354 ``` 355 void KwsManager::ConsumeSamples() 356 { 357 uintptr_t sampleAddr = 0; 358 size_t sampleSize = 0; 359 int32_t retCode = SUCCESS; 360 while (status_ == RUNNING) { 361 { 362 std::lock_guard<std::mutex> lock(mutex_); 363 if (cache_ == nullptr) { 364 printf("[KwsManager] cache_ is nullptr.\n"); 365 break; 366 } 367 sampleSize = cache_->GetCapturedBuffer(sampleAddr); 368 } 369 if (sampleSize == 0 || sampleAddr == 0) { 370 continue; 371 } 372 Array<int16_t> input = { 373 .data = (int16_t *)(sampleAddr), 374 .size = sampleSize >> 1 375 }; 376 { 377 std::lock_guard<std::mutex> lock(mutex_); 378 if (plugin_ == nullptr) { 379 printf("[KwsManager] cache_ is nullptr.\n"); 380 break; 381 } 382 if ((retCode = plugin_->SyncExecute(input)) != SUCCESS) { 383 printf("[KwsManager] SyncExecute KWS failed with retCode = [%d]\n", retCode); 384 continue; 385 } 386 } 387 } 388 } 389 ``` 390 391 Call the **Destroy** API. 392 393 ``` 394 void KwsManager::StopInference() 395 { 396 printf("[KwsManager] StopInference\n"); 397 if (plugin_ != nullptr) { 398 int ret = plugin_->Destroy(); 399 if (ret != SUCCESS) { 400 printf("[KwsManager] plugin_ destroy failed.\n"); 401 } 402 plugin_ = nullptr; 403 } 404 } 405 ``` 406 407 408## Repositories Involved 409 410[AI subsystem](https://gitee.com/openharmony/docs/blob/master/en/readme/ai.md) 411 412[ai_engine](https://gitee.com/openharmony/ai_engine) 413 414## Dependency Repositories 415 416[build\_lite](https://gitee.com/openharmony/build_lite/blob/master/README.md) 417 418[systemabilitymgr\_samgr\_lite](https://gitee.com/openharmony/systemabilitymgr_samgr_lite/blob/master/README.md) 419 420[startup\_init\_lite](https://gitee.com/openharmony/startup_init_lite/blob/master/README.md) 421 422## Reference 423 424[AI Framework Development Guide](https://gitee.com/openharmony/docs/blob/master/en/device-dev/subsystems/subsys-ai-aiframework-devguide.md) 425 426 427