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 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 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. For details, see the [keyword spotting demo](https://gitee.com/openharmony/applications_sample_camera/tree/41e463c4085f00ce18dd993ce865d624209a7513/ai/asr/keyword_spotting). 319 320 Directory: //applications/sample/camera/ai/asr/keyword\_spotting 321 322 Call the **Create** API. 323 324 ``` 325 bool KwsManager::PreparedInference() 326 { 327 if (capturer_ == nullptr) { 328 printf("[KwsManager] only load plugin after AudioCapturer ready\n"); 329 return false; 330 } 331 if (plugin_ != nullptr) { 332 printf("[KwsManager] stop created InferencePlugin at first\n"); 333 StopInference(); 334 } 335 plugin_ = std::make_shared<KWSSdk>(); 336 if (plugin_ == nullptr) { 337 printf("[KwsManager] fail to create inferencePlugin\n"); 338 return false; 339 } 340 if (plugin_->Create() != SUCCESS) { 341 printf("[KwsManager] KWSSdk fail to create.\n"); 342 return false; 343 } 344 std::shared_ptr<KWSCallback> callback = std::make_shared<MyKwsCallback>(); 345 if (callback == nullptr) { 346 printf("[KwsManager] new Callback failed.\n"); 347 return false; 348 } 349 plugin_->SetCallback(callback); 350 return true; 351 } 352 ``` 353 354 Call the **SyncExecute** API. 355 356 ``` 357 void KwsManager::ConsumeSamples() 358 { 359 uintptr_t sampleAddr = 0; 360 size_t sampleSize = 0; 361 int32_t retCode = SUCCESS; 362 while (status_ == RUNNING) { 363 { 364 std::lock_guard<std::mutex> lock(mutex_); 365 if (cache_ == nullptr) { 366 printf("[KwsManager] cache_ is nullptr.\n"); 367 break; 368 } 369 sampleSize = cache_->GetCapturedBuffer(sampleAddr); 370 } 371 if (sampleSize == 0 || sampleAddr == 0) { 372 continue; 373 } 374 Array<int16_t> input = { 375 .data = (int16_t *)(sampleAddr), 376 .size = sampleSize >> 1 377 }; 378 { 379 std::lock_guard<std::mutex> lock(mutex_); 380 if (plugin_ == nullptr) { 381 printf("[KwsManager] cache_ is nullptr.\n"); 382 break; 383 } 384 if ((retCode = plugin_->SyncExecute(input)) != SUCCESS) { 385 printf("[KwsManager] SyncExecute KWS failed with retCode = [%d]\n", retCode); 386 continue; 387 } 388 } 389 } 390 } 391 ``` 392 393 Call the **Destroy** API. 394 395 ``` 396 void KwsManager::StopInference() 397 { 398 printf("[KwsManager] StopInference\n"); 399 if (plugin_ != nullptr) { 400 int ret = plugin_->Destroy(); 401 if (ret != SUCCESS) { 402 printf("[KwsManager] plugin_ destroy failed.\n"); 403 } 404 plugin_ = nullptr; 405 } 406 } 407 ``` 408 409 410## Repositories Involved 411 412[AI subsystem](https://gitee.com/openharmony/docs/blob/master/en/readme/ai.md) 413 414[ai_engine](https://gitee.com/openharmony/ai_engine) 415 416## Dependency Repositories 417 418[build\_lite](https://gitee.com/openharmony/build_lite/blob/master/README.md) 419 420[systemabilitymgr\_samgr\_lite](https://gitee.com/openharmony/systemabilitymgr_samgr_lite/blob/master/README.md) 421 422[startup\_init\_lite](https://gitee.com/openharmony/startup_init_lite/blob/master/README.md) 423 424## Reference 425 426[AI Framework Development Guide](https://gitee.com/openharmony/docs/blob/master/en/device-dev/subsystems/subsys-ai-aiframework-devguide.md) 427 428 429