• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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;amp;inputInfo, DataInfo &amp;amp;outputInfo) override;
89        int32_t SetOption(int optionType, const DataInfo &amp;amp;inputInfo) override;
90        int32_t GetOption(int optionType, const DataInfo &amp;amp;inputInfo, DataInfo &amp;amp;outputInfo) override;
91        int32_t SyncProcess(IRequest *request, IResponse *&amp;amp;response) override;
92        int32_t AsyncProcess(IRequest *request, IPluginCallback*callback) override;
93        int32_t Release(bool isFullUnload, long long transactionId, const DataInfo &amp;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