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 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 >**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. 189 190 ``` 191 int32_t KWSSdk::KWSSdkImpl::Create() 192 { 193 if (kwsHandle_ != INVALID_KWS_HANDLE) { 194 HILOGE("[KWSSdkImpl]The SDK has been created"); 195 return KWS_RETCODE_FAILURE; 196 } 197 if (InitComponents() != RETCODE_SUCCESS) { 198 HILOGE("[KWSSdkImpl]Fail to init sdk components"); 199 return KWS_RETCODE_FAILURE; 200 } 201 int32_t retCode = AieClientInit(configInfo_, clientInfo_, algorithmInfo_, nullptr); 202 if (retCode != RETCODE_SUCCESS) { 203 HILOGE("[KWSSdkImpl]AieClientInit failed. Error code[%d]", retCode); 204 return KWS_RETCODE_FAILURE; 205 } 206 if (clientInfo_.clientId == INVALID_CLIENT_ID) { 207 HILOGE("[KWSSdkImpl]Fail to allocate client id"); 208 return KWS_RETCODE_FAILURE; 209 } 210 DataInfo inputInfo = { 211 .data = nullptr, 212 .length = 0, 213 }; 214 DataInfo outputInfo = { 215 .data = nullptr, 216 .length = 0, 217 }; 218 retCode = AieClientPrepare(clientInfo_, algorithmInfo_, inputInfo, outputInfo, nullptr); 219 if (retCode != RETCODE_SUCCESS) { 220 HILOGE("[KWSSdkImpl]AieclientPrepare failed. Error code[%d]", retCode); 221 return KWS_RETCODE_FAILURE; 222 } 223 if (outputInfo.data == nullptr || outputInfo.length <= 0) { 224 HILOGE("[KWSSdkImpl]The data or length of output info is invalid"); 225 return KWS_RETCODE_FAILURE; 226 } 227 MallocPointerGuard<unsigned char> pointerGuard(outputInfo.data); 228 retCode = PluginHelper::UnSerializeHandle(outputInfo, kwsHandle_); 229 if (retCode != RETCODE_SUCCESS) { 230 HILOGE("[KWSSdkImpl]Get handle from inputInfo failed"); 231 return KWS_RETCODE_FAILURE; 232 } 233 return KWS_RETCODE_SUCCESS; 234 } 235 236 int32_t KWSSdk::KWSSdkImpl::SyncExecute(const Array<uint16_t> &audioInput) 237 { 238 intptr_t newHandle = 0; 239 Array<int32_t> kwsResult = { 240 .data = nullptr, 241 .size = 0 242 }; 243 DataInfo inputInfo = { 244 .data = nullptr, 245 .length = 0 246 }; 247 DataInfo outputInfo = { 248 .data = nullptr, 249 .length = 0 250 }; 251 int32_t retCode = PluginHelper::SerializeInputData(kwsHandle_, audioInput, inputInfo); 252 if (retCode != RETCODE_SUCCESS) { 253 HILOGE("[KWSSdkImpl]Fail to serialize input data"); 254 callback_->OnError(KWS_RETCODE_SERIALIZATION_ERROR); 255 return RETCODE_FAILURE; 256 } 257 retCode = AieClientSyncProcess(clientInfo_, algorithmInfo_, inputInfo, outputInfo); 258 if (retCode != RETCODE_SUCCESS) { 259 HILOGE("[KWSSdkImpl]AieClientSyncProcess failed. Error code[%d]", retCode); 260 callback_->OnError(KWS_RETCODE_PLUGIN_EXECUTION_ERROR); 261 return RETCODE_FAILURE; 262 } 263 if (outputInfo.data == nullptr || outputInfo.length <= 0) { 264 HILOGE("[KWSSdkImpl] The data or length of outputInfo is invalid. Error code[%d]", retCode); 265 callback_->OnError(KWS_RETCODE_NULL_PARAM); 266 return RETCODE_FAILURE; 267 } 268 MallocPointerGuard<unsigned char> pointerGuard(outputInfo.data); 269 retCode = PluginHelper::UnSerializeOutputData(outputInfo, newHandle, kwsResult); 270 if (retCode != RETCODE_SUCCESS) { 271 HILOGE("[KWSSdkImpl]UnSerializeOutputData failed. Error code[%d]", retCode); 272 callback_->OnError(KWS_RETCODE_UNSERIALIZATION_ERROR); 273 return retCode; 274 } 275 if (kwsHandle_ != newHandle) { 276 HILOGE("[KWSSdkImpl]The handle[%lld] of output data is not equal to the current handle[%lld]", 277 (long long)newHandle, (long long)kwsHandle_); 278 callback_->OnError(KWS_RETCODE_PLUGIN_SESSION_ERROR); 279 return RETCODE_FAILURE; 280 } 281 callback_->OnResult(kwsResult); 282 return RETCODE_SUCCESS; 283 } 284 285 int32_t KWSSdk::KWSSdkImpl::Destroy() 286 { 287 if (kwsHandle_ == INVALID_KWS_HANDLE) { 288 return KWS_RETCODE_SUCCESS; 289 } 290 DataInfo inputInfo = { 291 .data = nullptr, 292 .length = 0 293 }; 294 int32_t retCode = PluginHelper::SerializeHandle(kwsHandle_, inputInfo); 295 if (retCode != RETCODE_SUCCESS) { 296 HILOGE("[KWSSdkImpl]SerializeHandle failed. Error code[%d]", retCode); 297 return KWS_RETCODE_FAILURE; 298 } 299 retCode = AieClientRelease(clientInfo_, algorithmInfo_, inputInfo); 300 if (retCode != RETCODE_SUCCESS) { 301 HILOGE("[KWSSdkImpl]AieClientRelease failed. Error code[%d]", retCode); 302 return KWS_RETCODE_FAILURE; 303 } 304 retCode = AieClientDestroy(clientInfo_); 305 if (retCode != RETCODE_SUCCESS) { 306 HILOGE("[KWSSdkImpl]AieClientDestroy failed. Error code[%d]", retCode); 307 return KWS_RETCODE_FAILURE; 308 } 309 mfccProcessor_ = nullptr; 310 pcmIterator_ = nullptr; 311 callback_ = nullptr; 312 kwsHandle_ = INVALID_KWS_HANDLE; 313 return KWS_RETCODE_SUCCESS; 314 } 315 ``` 316 3174. Develop a sample application. For details, see the [keyword spotting demo](https://gitee.com/openharmony/applications_sample_camera/tree/master/ai). 318 319 Directory: //applications/sample/camera/ai/asr/keyword\_spotting 320 321 Call the **Create** API. 322 323 ``` 324 bool KwsManager::PreparedInference() 325 { 326 if (capturer_ == nullptr) { 327 printf("[KwsManager] only load plugin after AudioCapturer ready\n"); 328 return false; 329 } 330 if (plugin_ != nullptr) { 331 printf("[KwsManager] stop created InferencePlugin at first\n"); 332 StopInference(); 333 } 334 plugin_ = std::make_shared<KWSSdk>(); 335 if (plugin_ == nullptr) { 336 printf("[KwsManager] fail to create inferencePlugin\n"); 337 return false; 338 } 339 if (plugin_->Create() != SUCCESS) { 340 printf("[KwsManager] KWSSdk fail to create.\n"); 341 return false; 342 } 343 std::shared_ptr<KWSCallback> callback = std::make_shared<MyKwsCallback>(); 344 if (callback == nullptr) { 345 printf("[KwsManager] new Callback failed.\n"); 346 return false; 347 } 348 plugin_->SetCallback(callback); 349 return true; 350 } 351 ``` 352 353 Call the **SyncExecute** API. 354 355 ``` 356 void KwsManager::ConsumeSamples() 357 { 358 uintptr_t sampleAddr = 0; 359 size_t sampleSize = 0; 360 int32_t retCode = SUCCESS; 361 while (status_ == RUNNING) { 362 { 363 std::lock_guard<std::mutex> lock(mutex_); 364 if (cache_ == nullptr) { 365 printf("[KwsManager] cache_ is nullptr.\n"); 366 break; 367 } 368 sampleSize = cache_->GetCapturedBuffer(sampleAddr); 369 } 370 if (sampleSize == 0 || sampleAddr == 0) { 371 continue; 372 } 373 Array<int16_t> input = { 374 .data = (int16_t *)(sampleAddr), 375 .size = sampleSize >> 1 376 }; 377 { 378 std::lock_guard<std::mutex> lock(mutex_); 379 if (plugin_ == nullptr) { 380 printf("[KwsManager] cache_ is nullptr.\n"); 381 break; 382 } 383 if ((retCode = plugin_->SyncExecute(input)) != SUCCESS) { 384 printf("[KwsManager] SyncExecute KWS failed with retCode = [%d]\n", retCode); 385 continue; 386 } 387 } 388 } 389 } 390 ``` 391 392 Call the **Destroy** API. 393 394 ``` 395 void KwsManager::StopInference() 396 { 397 printf("[KwsManager] StopInference\n"); 398 if (plugin_ != nullptr) { 399 int ret = plugin_->Destroy(); 400 if (ret != SUCCESS) { 401 printf("[KwsManager] plugin_ destroy failed.\n"); 402 } 403 plugin_ = nullptr; 404 } 405 } 406 ``` 407 408 409## Repositories Involved 410 411[AI subsystem](https://gitee.com/openharmony/docs/blob/master/en/readme/ai.md) 412 413[ai_engine](https://gitee.com/openharmony/ai_engine) 414 415## Dependency Repositories 416 417[build\_lite](https://gitee.com/openharmony/build_lite/blob/master/README.md) 418 419[systemabilitymgr\_samgr\_lite](https://gitee.com/openharmony/systemabilitymgr_samgr_lite/blob/master/README.md) 420 421[startup\_init\_lite](https://gitee.com/openharmony/startup_init_lite/blob/master/README.md) 422 423## Reference 424 425[AI Framework Development Guide](https://gitee.com/openharmony/docs/blob/master/en/device-dev/subsystems/subsys-ai-aiframework-devguide.md) 426 427 428