diff --git a/ohos/.gitignore b/ohos/.gitignore
new file mode 100644
index 00000000..ac668a0b
--- /dev/null
+++ b/ohos/.gitignore
@@ -0,0 +1,13 @@
+/node_modules
+/oh_modules
+/local.properties
+/.idea
+**/build
+/.hvigor
+.cxx
+/.clangd
+/.clang-format
+/.clang-tidy
+**/.test
+/.appanalyzer
+/.vscode
\ No newline at end of file
diff --git a/ohos/AppScope/app.json5 b/ohos/AppScope/app.json5
new file mode 100644
index 00000000..4b98ef7c
--- /dev/null
+++ b/ohos/AppScope/app.json5
@@ -0,0 +1,10 @@
+{
+ "app": {
+ "bundleName": "com.example.myapplication",
+ "vendor": "example",
+ "versionCode": 1000000,
+ "versionName": "1.0.0",
+ "icon": "$media:app_icon",
+ "label": "$string:app_name"
+ }
+}
diff --git a/ohos/AppScope/resources/base/element/string.json b/ohos/AppScope/resources/base/element/string.json
new file mode 100644
index 00000000..d71e5ead
--- /dev/null
+++ b/ohos/AppScope/resources/base/element/string.json
@@ -0,0 +1,8 @@
+{
+ "string": [
+ {
+ "name": "app_name",
+ "value": "MyApplication"
+ }
+ ]
+}
diff --git a/ohos/AppScope/resources/base/media/app_icon.png b/ohos/AppScope/resources/base/media/app_icon.png
new file mode 100644
index 00000000..cd45accb
Binary files /dev/null and b/ohos/AppScope/resources/base/media/app_icon.png differ
diff --git a/ohos/LICENSE b/ohos/LICENSE
new file mode 100644
index 00000000..ade750b1
--- /dev/null
+++ b/ohos/LICENSE
@@ -0,0 +1,177 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
diff --git a/ohos/OAT.xml b/ohos/OAT.xml
new file mode 100644
index 00000000..8ef903f2
--- /dev/null
+++ b/ohos/OAT.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ohos/README.OpenSource b/ohos/README.OpenSource
new file mode 100644
index 00000000..94689303
--- /dev/null
+++ b/ohos/README.OpenSource
@@ -0,0 +1,11 @@
+[
+ {
+ "Name": "json",
+ "License": "MIT License",
+ "License File": "LICENSE.MIT",
+ "Version Number": "master",
+ "Owner" : "xiafeng@huawei.com",
+ "Upstream URL": "https://github.com/nlohmann/json",
+ "Description": "C++解析json库"
+ }
+]
\ No newline at end of file
diff --git a/ohos/README.md b/ohos/README.md
new file mode 100644
index 00000000..6e940c4c
--- /dev/null
+++ b/ohos/README.md
@@ -0,0 +1,285 @@
+
+ OHOS-VAP
+
+
+
+ English | Spanish | German |
+French | 简体中文 | 日本語
+
+
+
+
+
+
+
+In the era of digital entertainment and online interaction, the quality of visual effects directly impacts user experience. `OHOS-VAP` is a powerful animation particle effect rendering component built on `OpenHarmony`, utilizing `OpenGL` technology and special algorithms. It not only provides stunning animation effects for applications but also creates an immersive visual experience for users.
+
+
+
+### Key Features
+
+- Compared to Webp and Apng animated image formats, it offers advantages such as higher compression rates (smaller file sizes) and hardware decoding (faster decoding).
+- Compared to Lottie, it can achieve more complex animation effects (such as particle effects).
+- High-performance rendering: With the powerful capabilities of OpenGL, OHOS-VAP achieves efficient particle effect rendering, ensuring a smooth user experience across various devices.
+- Easy integration: OHOS-VAP features a simple design that allows for easy integration with existing projects, helping developers quickly implement stunning animation effects and enhance the appeal of their applications.
+- Multi-platform support: Compatible with multiple devices, whether it's a smartphone, tablet, or computer, OHOS-VAP can provide consistent visual effects, aiding cross-terminal application development.
+
+### Application Scenarios
+
+- Live Room Effects: On major short video platforms such as Douyin, Kuaishou, Dewu, and Penguin Esports, use OHOS-VAP to add cool gift effects to live rooms, enhancing audience interaction and increasing the fun of live broadcasts.
+- E-commerce Promotion: In the gaming sector and on e-commerce platforms, use OHOS-VAP to create stunning product display effects that attract user attention and drive sales conversions.
+- Enhanced Gaming Experience: Add particle effects to game scenes, improving the overall gaming experience and immersing players in a more vivid virtual world.
+ 
+
+### Build Dependencies
+
+- IDE Version: DevEco Studio 5.0.1.403
+- SDK Version: ohos_sdk_public 5.0.0 (API Version 12 Release)
+- Store a video file named `1.mp4` in the specified path on the terminal device: `/storage/Users/currentUser/Documents` (**demo video is stored in the `video_demo` directory at the root**).
+- Developers can call the `this.xComponentContext.play()` interface to implement custom video parameter paths (supports network URLs).
+
+#### C/C++ Layer Directory Structure
+
+```
+├─include # Header files for masks, mixing, renderers, and utility classes
+│ ├─mask
+│ ├─mix
+│ ├─render
+│ └─util
+├─manager # xcomponent lifecycle management
+├─mask # Implementation of masks
+├─mix # Implementation of mixing
+├─napi # Napi layer functionality encapsulation
+├─render # Implementation of renderers
+├─types # Interface declarations
+│ └─libvap # Interface declarations for so files
+└─util # Implementation of utility classes
+```
+
+### Building the Project to Generate a Har Package
+
+To start the project, first run the command to generate a Har package as shown below.
+
+#### Run the following command in the IDE's Terminal
+
+```bash
+hvigorw assembleHar --mode module -p module=vap_module@default -p product=default -p buildMode=release --no-daemon
+```
+
+This will generate a Har package in the directory `.\vap_module\build\default\outputs\default\vap_module.har`.
+
+### Starting the Project
+
+For testers, the project can be quickly started by connecting the device in the IDE and clicking a button to launch it.
+
+Follow the official process to add signature information to correctly install the test application on the terminal device.
+
+### Reference Steps
+
+For developers, the generated Har package can be imported into other projects.
+
+#### Run the following command in the IDE's Terminal
+
+```bash
+ohpm install .\vap_module\build\default\outputs\default\vap_module.har
+```
+Install the previously generated Har package.
+
+### Quick Start
+- Refer to the example code `.\示例代码.ets`, and note that the example needs to have internet access to run correctly the first time.
+
+#### Importing Header Files
+
+In the usage file, import the header files as follows:
+
+```typescript
+import { VAPPlayer } from 'vap_module';
+import { MixInputData } from 'vap_module';
+```
+
+#### Defining the VAPPlayer Component
+
+```typescript
+private vapPlayer: VAPPlayer | undefined = undefined;
+@State buttonEnabled: boolean = true; // This state controls whether the button can be clicked
+@State src: string = "/storage/Users/currentUser/Documents/1.mp4"; // This path can be a network path
+```
+
+#### Mixing Interface Declaration
+
+```typescript
+class MixInputDataBean implements MixInputData {
+ constructor(txt?: string, imgUri?: string) {
+ this.txt = txt || '';
+ this.imgUri = imgUri || '';
+ }
+
+ txt: string;
+ imgUri: string;
+}
+```
+
+#### Configuring the Network Resource Download Path
+
+```typescript
+// For specific usage, refer to the example code
+// Get the sandbox path
+let context: Context = getContext(this) as Context;
+let dir = context.filesDir;
+```
+
+#### Interface
+
+```typescript
+XComponent({
+ id: 'xcomponentId', // Unique identifier
+ type: 'surface',
+ libraryname: 'vap'
+})
+ .onLoad((xComponentContext?: object | Record void>) => {
+ if (xComponentContext) {
+ this.vapPlayer = new VAPPlayer();
+ this.vapPlayer.setContext(xComponentContext);
+ this.vapPlayer.sandDir = dir; // Set storage path
+ }
+ })
+ .backgroundColor(Color.Transparent)
+ .height('100%')
+ .visibility(this.buttonEnabled ? Visibility.Hidden : Visibility.Visible)
+ .width('80%');
+```
+
+#### Calling Interfaces (e.g., Button Click Events)
+
+```typescript
+this.vapPlayer?.play(this.src, opts, () => {
+ this.buttonEnabled = true;
+});
+```
+
+#### Usage
+
+##### Using the Play Interface
+
+```typescript
+let opts: Array = new Array();
+let opt: MixInputDataBean = new MixInputDataBean();
+opt.txt = "星河Harmony NEXT";
+opt.imgUri = "/storage/Users/currentUser/Documents/1.png"; // Ensure there is a 1.png resource in the documents directory
+opts.push(opt);
+opts.push(opt);
+opts.push(opt);
+this.buttonEnabled = false;
+this.vapPlayer?.play("/storage/Users/currentUser/Documents/1.mp4", opts, () => {
+ this.buttonEnabled = true;
+});
+```
+
+##### Using Pause
+
+```typescript
+this.vapPlayer?.pause();
+```
+
+##### Using Stop
+
+```typescript
+this.vapPlayer?.stop();
+```
+
+##### Listening for Gestures
+
+- During animation playback, if the play area is clicked and a mixed animation resource is clicked, the callback will return that resource (as a string).
+```typescript
+this.vapPlayer?.on('click', (state) => {
+ if (state) {
+ console.log('js get onClick: ' + state);
+ }
+});
+```
+
+##### Listening for Playback Lifecycle Changes
+
+```typescript
+this.vapPlayer?.on('stateChange', (state, ret) => {
+ if (state) {
+ console.log('js get on: ' + state);
+ if (ret) {
+ console.log('js get on frame: ' + JSON.stringify(ret));
+ }
+ }
+});
+```
+
+- The callback parameter `state` reflects the current playback status.
+```typescript
+enum VapState {
+ UNKNOWN,
+ READY,
+ START,
+ RENDER,
+ COMPLETE,
+ DESTROY,
+ FAILED
+}
+```
+- Parameter `ret`: When `state` is `RENDER` or `START`, it returns an `AnimConfig` object.
+- Parameter `ret`: When `state` is `FAILED`, it reflects the current error code.
+- Parameter `ret`: For other states, it will be `undefined`.
+
+```typescript
+onPageHide(): void {
+ console.log('[LIFECYCLE-Page] onPageHide');
+ this.vapPlayer?.stop();
+}
+```
+You can call the onPageHide method in the page lifecycle
+
+#### **Permission Settings**
+
+**Note on Permission Settings**
+
+Permissions need to be added in the application's module `module.json5`, for example: `entry\src\main\module.json5`.
+
+```json
+"requestPermissions": [
+{
+"name": 'ohos.permission.READ_MEDIA',
+"reason": '$string:read_file',
+"usedScene": {
+"abilities": [
+"EntryAbility"
+],
+"when": "always"
+}
+},
+{
+"name": 'ohos.permission.WRITE_MEDIA',
+"reason": '$string:read_file',
+"usedScene": {
+"abilities": [
+"EntryAbility"
+],
+"when": "always"
+}
+},
+{
+"name": "ohos.permission.INTERNET"
+}
+]
+```
+
+### Compilation and Build
+
+- After successfully creating the project, to build, run `Build -> Build Hap(s)/APP(s) -> build App(s)` option.
+- The `hap` package will be generated in `/entry/build/default/outputs`.
+- Sign and install the generated `hap` package.
+
+### Test Demo
+
+Click the `Play` button to test the animation effect, and click again to enter loop playback.
\ No newline at end of file
diff --git a/ohos/README_jp.md b/ohos/README_jp.md
new file mode 100644
index 00000000..70a8e475
--- /dev/null
+++ b/ohos/README_jp.md
@@ -0,0 +1,275 @@
+
+ OHOS-VAP
+
+
+
+ English | 简体中文 | 日本語
+
+
+
+
+
+
+
+デジタルエンターテインメントとオンラインインタラクションの時代において、視覚効果の美しさはユーザー体験に直接影響を与えます。`OHOS-VAP` は `OpenHarmony` を基盤にし、`OpenGL` 技術と特別なアルゴリズムを用いて構築された強力なアニメーションパーティクルエフェクトレンダリングコンポーネントです。これは、アプリケーションに驚くべきアニメーション効果を提供するだけでなく、ユーザーに没入感のある視覚体験を創出します。
+
+
+
+### 主な特徴
+
+- WebpやApngの動きの提案に比べ、高圧縮率(素材が小さい)、ハードウェアデコード(デコードが速い)という利点があります。
+- Lottieに比べ、より複雑なアニメーション効果(例:パーティクルエフェクト)を実現できます。
+- 高性能レンダリング:OpenGLの強力な機能により、OHOS-VAPは効率的なパーティクルエフェクトのレンダリングを実現し、スムーズなユーザー体験を保証し、さまざまなデバイスに適しています。
+- 簡単な統合:OHOS-VAPはシンプルなデザインで、既存のプロジェクトと簡単に統合でき、開発者がクールなアニメーション効果を迅速に実現し、アプリの魅力を高めるのに役立ちます。
+- マルチプラットフォームサポート:複数のデバイスに対応しており、スマートフォン、タブレット、コンピュータのいずれでも、OHOS-VAPは一貫した視覚効果を提供し、クロステンデンアプリケーション開発を支援します。
+
+### アプリケーションシーン
+
+- ライブ配信のエフェクト:TikTok、快手、得物、ペンギンeスポーツなどの短編動画プラットフォームで、OHOS-VAPを利用してライブ配信にクールなギフトエフェクトを追加し、視聴者のインタラクション体験を向上させ、ライブ配信の楽しさを増します。
+- 電子商取引のプロモーション:ゲーム分野や電子商取引プラットフォームのイベントで、OHOS-VAPを使用して驚くべき製品展示効果を実現し、ユーザーの目を引き、販売転換を促進します。
+- ゲーム体験の向上:ゲームシーンにパーティクルエフェクトを追加し、全体的なゲーム体験を向上させ、プレイヤーをより生き生きとした仮想世界に没入させます。
+ 
+
+### ビルド依存関係
+
+- IDEバージョン:DevEco Studio 5.0.1.403
+- SDKバージョン:ohos_sdk_public 5.0.0 (API Version 12 Release)
+- ターミナルデバイスの指定パス`/storage/Users/currentUser/Documents`に`1.mp4`という名前の動画ファイルを保存します(**デモ動画はルートディレクトリの`video_demo`に保存されています**)。
+- 開発者は`this.xComponentContext.play()`インターフェースを呼び出してカスタム動画パラメータパスを実現できます(ネットワークURLをサポート)。
+
+#### C/C++層のディレクトリ構造
+
+```
+├─include # マスク、ミックス、レンダラー、ツールクラスのヘッダーファイルを保存
+│ ├─mask
+│ ├─mix
+│ ├─render
+│ └─util
+├─manager # xcomponentのライフサイクル管理
+├─mask # マスクの実装
+├─mix # ミックスの実装
+├─napi # Napi層の機能のラッピング
+├─render # レンダラーの実装
+├─types # インターフェース宣言
+│ └─libvap # soファイルのインターフェース宣言
+└─util # ツールクラスの実装
+```
+
+### プロジェクトビルド生成 Har パッケージ
+
+プロジェクトを開いたら、最初にコマンドを実行してHarパッケージを生成します。以下を参考にしてください。
+
+#### IDEのターミナルで以下のコマンドを実行
+
+```bash
+hvigorw assembleHar --mode module -p module=vap_module@default -p product=default -p buildMode=release --no-daemon
+```
+
+`.\vap_module\build\default\outputs\default\vap_module.har`ディレクトリにHarパッケージが生成されます。
+
+### プロジェクトの起動
+
+テスト担当者は、IDEでデバイスを接続した後、ワンクリックでプロジェクトを迅速に起動できます。
+
+公式の手順に従って署名情報を追加すれば、テストアプリを正しくターミナルデバイスにインストールできます。
+
+### 参照手順
+
+開発者は、生成したHarパッケージをのプロジェクトに取り込むことができます。
+
+#### IDEのターミナルで以下のコマンドを実行
+
+```bash
+ohpm install .\vap_module\build\default\outputs\default\vap_module.har
+```
+以前に生成したHarパッケージをインストールします。
+
+### 迅速な使用
+- サンプルコード`.\サンプルコード.ets`を参考にできます。サンプルが初めて正常に動作するには、ネットワークを開く必要があります。
+
+#### ヘッダーファイルのインポート
+
+使用するファイルにヘッダーファイルをインポートします。
+
+```typescript
+import { VAPPlayer } from 'vap_module';
+import { MixInputData } from 'vap_module';
+```
+
+#### VAPPlayerコンポーネントの定義
+
+```typescript
+private vapPlayer: VAPPlayer | undefined = undefined;
+@State buttonEnabled: boolean = true; // この状態はボタンのクリック可能性を制御します
+@State src: string = "/storage/Users/currentUser/Documents/1.mp4"; // このパスはネットワークパスでも可能です
+```
+
+#### ミックスインターフェース宣言
+
+```typescript
+class MixInputDataBean implements MixInputData {
+ constructor(txt?: string, imgUri?: string) {
+ this.txt = txt || '';
+ this.imgUri = imgUri || '';
+ }
+
+ txt: string;
+ imgUri: string;
+}
+```
+
+#### ネットワークリソースダウンロードパスの設定
+```typescript
+// 具体的な使用法はサンプルコードを参考にしてください
+// サンドボックスパスを取得
+let context : Context = getContext(this) as Context
+let dir = context.filesDir
+```
+
+#### インターフェース
+
+```typescript
+XComponent({
+ id: 'xcomponentId', // ユニーク識別子
+ type: 'surface',
+ libraryname: 'vap'
+})
+ .onLoad((xComponentContext?: object | Record void>) => {
+ if (xComponentContext) {
+ this.vapPlayer = new VAPPlayer
+ this.vapPlayer.setContext(xComponentContext)
+ this.vapPlayer.sandDir = dir // ストレージパスを設定
+ }
+ })
+ .backgroundColor(Color.Transparent)
+ .height('100%')
+ .visibility(this.buttonEnabled ? Visibility.Hidden: Visibility.Visible)
+ .width('80%')
+```
+
+#### インターフェースの呼び出し(例えばボタンのクリックイベント)
+
+```typescript
+this.vapPlayer?.play(this.src, opts, () => {
+ this.buttonEnabled = true;
+});
+```
+
+#### 使用
+
+##### プレイインターフェースPlayの使用
+
+```typescript
+let opts: Array = new Array
+let opt: MixInputDataBean = new MixInputDataBean
+opt.txt = "星河Harmony NEXT"
+opt.imgUri = "/storage/Users/currentUser/Documents/1.png" // ドキュメントディレクトリに1.pngリソースが必要です
+opts.push(opt)
+opts.push(opt)
+opts.push(opt)
+this.buttonEnabled = false;
+this.vapPlayer?.play("/storage/Users/currentUser/Documents/1.mp4", opts, () => {
+ this.buttonEnabled = true;
+});
+```
+
+##### 一時停止の使用
+
+```typescript
+this.vapPlayer?.pause()
+```
+
+##### 停止の使用
+
+```typescript
+this.vapPlayer?.stop()
+```
+
+##### ジェスチャーのリスニング
+
+- アニメーション再生中に再生領域をクリックすると、融合アニメーションリソースにクリックが当たった場合、コールバックがそのリソース(文字列)を返します。
+```typescript
+this.vapPlayer?.on('click', (state)=>{
+ if(state) {
+ console.log('js get onClick: ' + state)
+ }
+})
+```
+
+##### 再生ライフサイクルの変化をリスニング
+
+```typescript
+this.vapPlayer?.on('stateChange', (state, ret)=>{
+ if(state) {
+ console.log('js get on: ' + state)
+ if(ret)
+ console.log('js get on frame: ' + JSON.stringify(ret))
+ }
+})
+```
+
+- コールバックパラメータ `state` は現在の再生状態を反映します
+```typescript
+enum VapState {
+ UNKNOWN,
+ READY,
+ START,
+ RENDER,
+ COMPLETE,
+ DESTROY,
+ FAILED
+}
+```
+- パラメータ `ret` は、`state` が `RENDER` または `START` の場合に `AnimConfig` オブジェクトを返します
+- パラメータ `ret` は、`state` が `FAILED` の場合に現在のエラーコードを反映します
+- パラメータ `ret` は、の状態では `undefined` になります
+
+
+#### **権限設定**
+
+**権限の設定に注意してください**
+
+アプリモジュールの`module.json5`に権限を追加する必要があります。例えば:`entry\src\main\module.json5`
+
+```json
+"requestPermissions": [
+{
+"name": 'ohos.permission.READ_MEDIA',
+"reason": '$string:read_file',
+"usedScene": {
+"abilities": [
+"EntryAbility"
+],
+"when": "always"
+}
+},
+{
+"name": 'ohos.permission.WRITE_MEDIA',
+"reason": '$string:read_file',
+"usedScene": {
+"abilities": [
+"EntryAbility"
+],
+"when": "always"
+}
+},
+{
+"name": "ohos.permission.INTERNET"
+}
+]
+```
+
+### コンパイルビルド
+
+- プロジェクトが正常に作成された後、ビルドを行うには `Build -> Build Hap(s)/APP(s) -> build App(s)` オプションを実行します。
+- `/entry/build/default/outputs` に `hap` パッケージが生成されます。
+- 生成された `hap` パッケージに署名してインストールします。
+
+### テストデモ
+
+`Play` ボタンをクリックしてアニメーション効果をテストし、再度クリックすることでループ再生に入ります。
\ No newline at end of file
diff --git a/ohos/README_zh.md b/ohos/README_zh.md
new file mode 100644
index 00000000..6ecc4576
--- /dev/null
+++ b/ohos/README_zh.md
@@ -0,0 +1,289 @@
+
+ OHOS-VAP
+
+
+
+ English | 简体中文 | 日本語
+
+
+
+
+
+
+
+
+在数字娱乐和在线互动的时代,视觉效果的精美程度直接影响用户体验。`OHOS-VAP` 是一个基于 `OpenHarmony` 运用 `OpenGL` 技术和特殊算法打造的强大动画粒子特效渲染组件。它不仅能够为应用程序提供令人惊叹的动画效果,更为用户创造了一种身临其境的视觉享受。
+
+
+
+## 主要特征
+
+- 相比Webp, Apng动图方案,具有高压缩率(素材更小)、硬件解码(解码更快)的优点.
+- 相比Lottie,能实现更复杂的动画效果(比如粒子特效)
+- 高性能渲染:凭借 OpenGL 的强大能力,OHOS-VAP 实现了高效的粒子特效渲染,确保流畅的用户体验,适用于各种设备。
+- 易于集成:OHOS-VAP 设计简洁,易于与现有项目集成,帮助开发者快速实现酷炫的动画效果,提升应用的吸引力。
+- 多平台支持:兼容多个设备,无论是手机、平板还是电脑,OHOS-VAP 都能提供一致的视觉效果,助力跨终端应用开发。
+
+## 应用场景
+
+- 直播间特效:在各大短视频平台中如抖音,快手,得物,企鹅电竞,利用 OHOS-VAP 为直播间增添炫酷的礼物特效,提升观众的互动体验,增加直播的趣味性。
+- 电商活动推广:在游戏领域及电商平台的活动中,使用 OHOS-VAP 实现令人惊叹的产品展示效果,吸引用户眼球,推动销售转化。
+- 游戏体验提升:为游戏场景增添粒子特效,提升整体游戏体验,让玩家沉浸在更为生动的虚拟世界中。
+ 
+
+## 构建依赖
+
+- IDE版本:DevEco Studio 5.0.1.403
+- SDK版本:ohos_sdk_public 5.0.0 (API Version 12 Release)
+- 对于开发人员可以调用`this.xComponentContext.play()`接口来实现自定义视频传参路径(支持网路URL)
+
+### C/C++层目录结构
+
+```
+├─include # 遮罩 融合 渲染器 工具类头文件存放
+│ ├─mask
+│ ├─mix
+│ ├─render
+│ └─util
+├─manager # xcomponent 生命周期管理
+├─mask # 遮罩的实现
+├─mix # 融合实现
+├─napi # Napi 层功能的封装
+├─render # 渲染器的实现
+├─types # 接口声明
+│ └─libvap # so文件接口声明
+└─util # 工具类的实现0
+```
+## 构建工程生成 Har 包
+
+打开工程首先运行命令生成 Har 包请参考下方
+
+### 在 IDE 的 Terminal 运行以下命令
+
+```bash
+hvigorw assembleHar --mode module -p module=vap_module@default -p product=default -p buildMode=release --no-daemon
+```
+
+会在`.\vap_module\build\default\outputs\default\vap_module.har` 目录生成一个 Har 包
+
+## 启动项目
+
+对于测试人员可快速启动项目,直接在 IDE 连接设备后一键启动
+
+按照官方流程添加签名信息,就可正确安装测试应用到终端设备
+
+## 引用步骤
+
+对于开发人员,可把生成的 Har 包引入其工程
+
+### 在 IDE 的 Terminal 运行以下命令
+
+```bash
+ohpm install .\vap_module\build\default\outputs\default\vap_module.har
+```
+安装之前生成的 Har 包
+
+
+
+## 快速使用
+1. api模式 可参考示例代码 [api模式](./示例代码.ets)
+2. 组件模式 可参考示例代码 [组件模式](./组件模式.ets),使用更便捷
+
+
+### 头文件引入
+
+在使用文件中进行头文件的引入
+
+```typescript
+import { VAPPlayer,MixData } from 'vap_module';
+```
+
+### 定义 VAPPlayer 组件
+
+```typescript
+private vapPlayer: VAPPlayer | undefined = undefined;
+@State buttonEnabled: boolean = true; // 该状态为控制按钮是否可以点击
+@State src: string = "/storage/Users/currentUser/Documents/1.mp4"; // 该路径可为网络路径
+```
+
+### 配置网络资源下载路径
+```typescript
+// 具体使用可参考示例代码
+// 获取沙箱路径
+let context : Context = getContext(this) as Context
+let dir = context.filesDir
+```
+### 界面
+
+```typescript
+XComponent({
+ id: 'xcomponentId', // 唯一标识
+ type: 'surface',
+ libraryname: 'vap'
+})
+ .onLoad((xComponentContext?: object | Record void>) => {
+ if (xComponentContext) {
+ this.vapPlayer = new VAPPlayer
+ this.vapPlayer.setContext(xComponentContext)
+ this.vapPlayer.sandDir = dir // 设置存储路径
+ }
+ })
+ .backgroundColor(Color.Transparent)
+ .height('100%')
+ .visibility(this.buttonEnabled ? Visibility.Hidden: Visibility.Visible)
+ .width('80%')
+```
+### 设置视频对齐方式
+
+ 通过`setFitType`这个接口设置视频对齐方式(支持FIT_XY,FIT_CENTER,CENTER_CROP)
+
+
+ **接口需要在`play`之前使用**
+
+```typescript
+this.vapPlayer?.setFitType(fitType)
+```
+
+### 使用
+
+#### 播放接口 Play 的使用
+融合动画信息顺序自定义,需要指定 `tag`, `tag` 为视频制作时指定,该信息可通过`this.vapPlayer.getVideoInfo(uri)`
+当融合信息为字体时,可配置字体的对齐,颜色,大小
+```typescript
+let opts: Array = [{
+ tag: 'sImg1',
+ imgUri: getContext(this).filesDir + '/head1.png'
+}, {
+ tag: 'abc',
+ txt: "星河Harmony NEXT",
+ imgUri: getContext(this).filesDir + '/head1.png'
+}, {
+ tag: 'sTxt1',
+ txt: "星河Harmony NEXT",
+ textAlign: this.textAlign,
+ fontWeight: this.fontWeight,
+ color: this.color
+}];
+this.buttonEnabled = false;
+
+this.vapPlayer?.play(getContext(this).filesDir + "/vapx.mp4", opts, () => {
+ this.buttonEnabled = true;
+});
+```
+
+#### 暂停的使用
+
+```typescript
+this.vapPlayer?.pause()
+```
+
+#### 停止的使用
+
+```typescript
+this.vapPlayer?.stop()
+```
+
+#### 监听手势
+
+- 在动画播放过程中点击播放区域,如果点击到融合动画资源,回调会返回该资源(字符串)
+- **接口需要在`play`之前使用**
+```typescript
+this.vapPlayer?.on('click', (state)=>{
+ if(state) {
+ console.log('js get onClick: ' + state)
+ }
+})
+```
+
+#### 监听播放生命周期变化
+**接口需要在`play`之前使用**
+```typescript
+this.vapPlayer?.on('stateChange', (state, ret)=>{
+ if(state) {
+ console.log('js get on: ' + state)
+ if(ret)
+ console.log('js get on frame: ' + JSON.stringify(ret))
+ }
+})
+```
+
+- 回调参数 `state` 反应当前播放的状态
+```typescript
+enum VapState {
+ UNKNOWN,
+ READY,
+ START,
+ RENDER,
+ COMPLETE,
+ DESTROY,
+ FAILED
+}
+```
+- 参数 `ret` ,当 `state` 为 `RENDER` 或 `START` 返回 `AnimConfig` 对象
+- 参数 `ret` ,当 `state` 为 `FAILED` 反应当前的错误码
+- 参数 `ret` ,其余状态为 `undefined`
+
+#### 应用退出后台
+
+```typescript
+ onPageHide(): void {
+ console.log('[LIFECYCLE-Page] onPageHide');
+ this.vapPlayer?.stop()
+ }
+```
+可在页面的生命周期中调用`onPageHide`方法
+
+#### 兼容老视频(alphaplayer 对称的视频)
+```typescript
+this.vapPlayer?.setVideoMode(VideoMode.VIDEO_MODE_SPLIT_HORIZONTAL)
+```
+对于老视频推荐调用这个接口,**接口需要在`play`之前使用**
+
+### **权限设置**
+
+**注意权限的设置**
+
+需要在应用模块的`module.json5`中添加权限 例如:`entry\src\main\module.json5`
+
+```json
+"requestPermissions": [
+{
+"name": 'ohos.permission.READ_MEDIA',
+"reason": '$string:read_file',
+"usedScene": {
+"abilities": [
+"EntryAbility"
+],
+"when": "always"
+}
+},
+{
+"name": 'ohos.permission.WRITE_MEDIA',
+"reason": '$string:read_file',
+"usedScene": {
+"abilities": [
+"EntryAbility"
+],
+"when": "always"
+}
+},
+{
+"name": "ohos.permission.INTERNET"
+}
+]
+```
+
+## 编译构建
+
+- 工程创建成功后,构建请运行 `Build -> Build Hap(s)/APP(s) -> build App(s) `选项
+- `/entry/build/default/outputs` 生成 `hap` 包
+- 签名安装生成的 `hap` 包
+
+## 测试demo
+
+点击 `Play` 按钮,测试动画效果,再次点击进入循环播放
diff --git a/ohos/build-profile.json5 b/ohos/build-profile.json5
new file mode 100644
index 00000000..1815ec95
--- /dev/null
+++ b/ohos/build-profile.json5
@@ -0,0 +1,35 @@
+{
+ "app": {
+ "products": [
+ {
+ "name": "default",
+ "signingConfig": "default",
+ "compatibleSdkVersion": "5.0.0(12)",
+ "runtimeOS": "HarmonyOS"
+ }
+ ],
+ "buildModeSet": [
+ {
+ "name": "release"
+ }
+ ],
+ },
+ "modules": [
+ {
+ "name": "entry",
+ "srcPath": "./entry",
+ "targets": [
+ {
+ "name": "default",
+ "applyToProducts": [
+ "default"
+ ]
+ }
+ ]
+ },
+ {
+ "name": "vap_module",
+ "srcPath": "./vap_module",
+ }
+ ]
+}
\ No newline at end of file
diff --git a/ohos/entry/.gitignore b/ohos/entry/.gitignore
new file mode 100644
index 00000000..32abf8fd
--- /dev/null
+++ b/ohos/entry/.gitignore
@@ -0,0 +1,6 @@
+/node_modules
+/oh_modules
+/.preview
+/build
+/.cxx
+/.test
\ No newline at end of file
diff --git a/ohos/entry/build-profile.json5 b/ohos/entry/build-profile.json5
new file mode 100644
index 00000000..20e75968
--- /dev/null
+++ b/ohos/entry/build-profile.json5
@@ -0,0 +1,14 @@
+
+{
+ "apiType" : "stageMode",
+ "buildOption" : {
+ },
+ "targets" : [
+ {
+ "name" : "default"
+ },
+ {
+ "name" : "ohosTest"
+ }
+ ]
+}
diff --git a/ohos/entry/hvigorfile.ts b/ohos/entry/hvigorfile.ts
new file mode 100644
index 00000000..ba9480d0
--- /dev/null
+++ b/ohos/entry/hvigorfile.ts
@@ -0,0 +1,6 @@
+import { hapTasks } from '@ohos/hvigor-ohos-plugin';
+
+export default {
+ system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
+ plugins:[] /* Custom plugin to extend the functionality of Hvigor. */
+}
diff --git a/ohos/entry/obfuscation-rules.txt b/ohos/entry/obfuscation-rules.txt
new file mode 100644
index 00000000..3ae3d228
--- /dev/null
+++ b/ohos/entry/obfuscation-rules.txt
@@ -0,0 +1,18 @@
+# Define project specific obfuscation rules here.
+# You can include the obfuscation configuration files in the current module's build-profile.json5.
+#
+# For more details, see
+# https://gitee.com/openharmony/arkcompiler_ets_frontend/blob/master/arkguard/README.md
+
+# Obfuscation options:
+# -disable-obfuscation: disable all obfuscations
+# -enable-property-obfuscation: obfuscate the property names
+# -enable-toplevel-obfuscation: obfuscate the names in the global scope
+# -compact: remove unnecessary blank spaces and all line feeds
+# -remove-log: remove all console.* statements
+# -print-namecache: print the name cache that contains the mapping from the old names to new names
+# -apply-namecache: reuse the given cache file
+
+# Keep options:
+# -keep-property-name: specifies property names that you want to keep
+# -keep-global-name: specifies names that you want to keep in the global scope
\ No newline at end of file
diff --git a/ohos/entry/oh-package-lock.json5 b/ohos/entry/oh-package-lock.json5
new file mode 100644
index 00000000..5a3bf916
--- /dev/null
+++ b/ohos/entry/oh-package-lock.json5
@@ -0,0 +1,18 @@
+{
+ "meta": {
+ "stableOrder": true
+ },
+ "lockfileVersion": 3,
+ "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.",
+ "specifiers": {
+ "vap_module@../vap_module": "vap_module@../vap_module"
+ },
+ "packages": {
+ "vap_module@../vap_module": {
+ "name": "vap_module",
+ "version": "1.0.0",
+ "resolved": "../vap_module",
+ "registryType": "local"
+ }
+ }
+}
\ No newline at end of file
diff --git a/ohos/entry/oh-package.json5 b/ohos/entry/oh-package.json5
new file mode 100644
index 00000000..7b4f7ecb
--- /dev/null
+++ b/ohos/entry/oh-package.json5
@@ -0,0 +1,17 @@
+{
+ "license": "Apache-2.0",
+ "devDependencies": {},
+ "author": "",
+ "name": "entry",
+ "ohos": {
+ "org": "huawei",
+ "directoryLevel": "module",
+ "buildTool": "hvigor"
+ },
+ "description": "example description",
+ "main": "",
+ "version": "1.0.0",
+ "dependencies": {
+ "vap_module": "file:../vap_module"
+ }
+}
diff --git a/ohos/entry/src/main/ets/common/FileUtil.ts b/ohos/entry/src/main/ets/common/FileUtil.ts
new file mode 100644
index 00000000..f92340d9
--- /dev/null
+++ b/ohos/entry/src/main/ets/common/FileUtil.ts
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2022 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { BusinessError } from '@kit.BasicServicesKit';
+import common from '@ohos.app.ability.common'
+
+import fs from '@ohos.file.fs';
+import { http } from '@kit.NetworkKit';
+
+export class FileUtil {
+
+ copyRawfileToContext(context:common.UIAbilityContext) {
+ let filePath = context.filesDir;
+ try {
+ context.resourceManager.getRawFileList("").then((value: Array) => {
+ value.forEach(fileName => {
+ let fileContent: ArrayBufferLike = context.resourceManager.getRawFileContentSync(fileName).buffer;
+ let newFile = fs.openSync(filePath + "/" + fileName, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
+ fs.writeSync(newFile.fd, fileContent);
+ fs.close(newFile);
+ });
+
+ }).catch((error: BusinessError) => {
+ console.error(`promise getRawFileList failed, error code: ${error.code}, message: ${error.message}.`);
+ });
+ } catch (error) {
+ let code = (error as BusinessError).code;
+ let message = (error as BusinessError).message;
+ console.error(`promise getRawFileList failed, error code: ${code}, message: ${message}.`);
+ }
+ }
+
+ async downloadFile(url:string,context:common.UIAbilityContext): Promise{
+ const fileName = this.extractFileName(url);
+ let filePath = context.filesDir+"/" + fileName
+ let res = fs.accessSync(filePath);
+ if (res) {
+ return filePath;
+ }
+ let httpRequest = http.createHttp();
+ try {
+ let data = await httpRequest.request(url, {
+ method: http.RequestMethod.GET,
+ expectDataType: http.HttpDataType.ARRAY_BUFFER,
+ })
+ let buf:ArrayBuffer = data.result as ArrayBuffer;
+ let file = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
+ fs.writeSync(file.fd, buf);
+ fs.closeSync(file);
+ } catch (err) {
+ console.error("accessSync failed with error message: " + err.message + ", error code: " + err.code);
+ filePath = ''
+ } finally {
+ httpRequest.destroy();
+ }
+ return filePath
+ }
+
+ extractFileName(url: string): string {
+ const matches = url.match(/\/([^\/?#]+)[^\/]*$/);
+ if (matches && matches.length > 1) {
+ return matches[1];
+ }
+ return '';
+ }
+
+}
+
+
diff --git a/ohos/entry/src/main/ets/common/LogUtil.ets b/ohos/entry/src/main/ets/common/LogUtil.ets
new file mode 100644
index 00000000..a0e195dc
--- /dev/null
+++ b/ohos/entry/src/main/ets/common/LogUtil.ets
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2022 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+export class LogUtil {
+ public static OFF: number = 1
+ public static LOG: number = 2
+ public static DEBUG: number = 3
+ public static INFO: number = 4
+ public static WARN: number = 5
+ public static ERROR: number = 6
+ public static ALL: number = 7
+ public static mLogLevel:number = LogUtil.ALL;
+
+ public static debug(message: string, ...args: Object[]) {
+ if (LogUtil.mLogLevel >= LogUtil.DEBUG) {
+ console.debug(message, args)
+ }
+ }
+
+ public static info(message: string, ...args: Object[]) {
+ if (LogUtil.mLogLevel >= LogUtil.INFO) {
+ console.info(message, args)
+ }
+ }
+
+ public static log(message: string, ...args: Object[]) {
+ if (LogUtil.mLogLevel >= LogUtil.LOG) {
+ console.log(message, args)
+ }
+ }
+
+ public static warn(message: string, ...args: Object[]) {
+ if (LogUtil.mLogLevel >= LogUtil.WARN) {
+ console.warn(message, args)
+ }
+ }
+
+ // error 不做拦截
+ public static error(message: string, ...args: Object[]) {
+ if(LogUtil.mLogLevel >= LogUtil.ERROR) {
+ console.error(message, args)
+ }
+ }
+}
\ No newline at end of file
diff --git a/ohos/entry/src/main/ets/entryability/EntryAbility.ts b/ohos/entry/src/main/ets/entryability/EntryAbility.ts
new file mode 100644
index 00000000..3881ad16
--- /dev/null
+++ b/ohos/entry/src/main/ets/entryability/EntryAbility.ts
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2023 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import UIAbility from '@ohos.app.ability.UIAbility';
+import window from '@ohos.window';
+import hilog from '@ohos.hilog';
+import nativerender from 'libvap.so';
+import { FileUtil } from '../common/FileUtil'
+export enum ContextType {
+ APP_LIFECYCLE,
+ PAGE_LIFECYCLE,
+}
+
+const nativeAppLifecycle = nativerender.getContext(ContextType.APP_LIFECYCLE as any);
+
+export default class EntryAbility extends UIAbility {
+ onCreate(want, launchParam) {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
+ nativeAppLifecycle.onCreate();
+ globalThis.abilityWant = want;
+ let fileUtil = new FileUtil();
+ fileUtil.copyRawfileToContext(this.context);
+ }
+
+ onDestroy() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
+ nativeAppLifecycle.onDestroy();
+ }
+
+ onWindowStageCreate(windowStage: window.WindowStage) {
+ // Main window is created, set main page for this ability
+ hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
+ nativeAppLifecycle.onShow();
+ windowStage.loadContent('pages/Index', (err, data) => {
+ if (err.code) {
+ hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
+ return;
+ }
+ hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
+ });
+ }
+
+ onWindowStageDestroy() {
+ // Main window is destroyed, release UI related resources
+ hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
+ nativeAppLifecycle.onHide();
+ }
+
+ onForeground() {
+ // Ability has brought to foreground
+ hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
+ }
+
+ onBackground() {
+ // Ability has back to background
+ hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');
+ }
+};
diff --git a/ohos/entry/src/main/ets/pages/GeneralAnimation.ets b/ohos/entry/src/main/ets/pages/GeneralAnimation.ets
new file mode 100644
index 00000000..67c99551
--- /dev/null
+++ b/ohos/entry/src/main/ets/pages/GeneralAnimation.ets
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { VAPPlayer } from 'vap_module';
+import { systemDateTime } from '@kit.BasicServicesKit';
+import { LogUtil } from '../common/LogUtil';
+import { util } from '@kit.ArkTS';
+import { VAPComponent, FitType, MixData, VideoMode } from 'vap_module';
+
+@Entry
+@Component
+struct GeneralAnimation {
+ private vapPlayer: VAPPlayer | undefined = undefined;
+ @State buttonEnabled: boolean = true;
+ @State fitType: FitType = FitType.FIT_CENTER;
+ private playTimes: number = 0;
+ private stopTimes: number = 0;
+ private pauseTimes: number = 0;
+
+ aboutToDisappear(): void {
+ LogUtil.info('aboutToDisappear')
+ }
+
+ onPageShow(): void {
+ LogUtil.info('onPageShow')
+ }
+
+ onPageHide(): void {
+ LogUtil.info('onPageHide')
+ this.vapPlayer?.stop()
+ }
+
+ play() {
+ if (this.vapPlayer === undefined) {
+ LogUtil.error('plz create XComponent first')
+ return
+ }
+ this.vapPlayer?.setFitType(this.fitType)
+ this.vapPlayer?.setVideoMode(VideoMode.VIDEO_MODE_SPLIT_HORIZONTAL)
+ try {
+ let opts: Array = new Array();
+ this.buttonEnabled = false;
+
+ let startTime = systemDateTime.getTime(true)
+
+ this.vapPlayer?.play(getContext(this).filesDir + "/great_gift.mp4", opts, () => {
+ LogUtil.info("js get callback")
+ this.buttonEnabled = true;
+ });
+ let endTime = systemDateTime.getTime(true)
+ LogUtil.info(this.playTimes + " play cost " + (endTime - startTime) / 1000)
+ } catch (e) {
+ LogUtil.error(this.playTimes + " play error " + JSON.stringify(e));
+ }
+ this.playTimes++;
+ }
+
+ pause() {
+ try {
+ let startTime = systemDateTime.getTime(true);
+ this.vapPlayer?.pause();
+ let endTime = systemDateTime.getTime(true)
+ LogUtil.info(this.pauseTimes + " pause cost " + (endTime - startTime) / 1000)
+ } catch (e) {
+ LogUtil.error(this.pauseTimes + " pause error " + JSON.stringify(e));
+ }
+ this.pauseTimes++;
+ }
+
+ stop() {
+ try {
+ let startTime = systemDateTime.getTime(true);
+ this.vapPlayer?.stop();
+ let endTime = systemDateTime.getTime(true)
+ LogUtil.info(this.stopTimes + " stop cost " + (endTime - startTime) / 1000)
+ } catch (e) {
+ LogUtil.error(this.stopTimes + " stop error " + JSON.stringify(e));
+ }
+ this.stopTimes++;
+ }
+
+ build() {
+ Column() {
+ Stack() {
+ XComponent({
+ id: 'xcomponentId_' + util.generateRandomUUID(),
+ type: 'surface',
+ libraryname: 'vap'
+ })
+ .onLoad((context?: object) => {
+ if (context) {
+ this.vapPlayer = new VAPPlayer;
+ this.vapPlayer.setContext(context);
+ }
+ })
+ .backgroundColor(Color.Transparent)
+ .width('100%')
+ .height('100%')
+ .visibility(this.buttonEnabled ? Visibility.Hidden : Visibility.Visible)
+ }.backgroundImage($r('app.media.bg'))
+ .backgroundImageSize(ImageSize.FILL)
+ .height('90%')
+ .width('100%')
+
+ Scroll() {
+ Row() {
+ Select([{ value: 'fix_xy' }, { value: 'fix_center' }, { value: 'center_crop' }])
+ .selected(1)
+ .value('fix_center')
+ .onSelect((index: number) => {
+ console.info('Select: ' + index)
+ this.fitType = index
+ })
+ Button('Play')
+ .fontWeight(500)
+ .onClick(() => {
+ this.play()
+ })
+ .width('200')
+ .enabled(this.buttonEnabled)
+ Button('pause')
+ .onClick(() => {
+ this.pause()
+ })
+ Button('stop')
+ .onClick(() => {
+ this.stop()
+ })
+ }
+ }
+ .width('100%')
+ .scrollable(ScrollDirection.Horizontal)
+ .padding({ bottom: 10 })
+ .margin({ left: 5, right: 5 })
+ .border({
+ color: Color.Grey,
+ style: BorderStyle.Solid,
+ radius: 5,
+ width: 2
+ })
+ .backgroundColor(Color.White)
+ }
+ }
+}
\ No newline at end of file
diff --git a/ohos/entry/src/main/ets/pages/Index.ets b/ohos/entry/src/main/ets/pages/Index.ets
new file mode 100644
index 00000000..6702eaf5
--- /dev/null
+++ b/ohos/entry/src/main/ets/pages/Index.ets
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import router from '@ohos.router';
+
+@Entry
+@Component
+struct Index{
+ build() {
+ Scroll() {
+ Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
+ Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
+ Button("测试普通动画")
+ .onClick(() => {
+ router.pushUrl({ url: "pages/GeneralAnimation" });
+ }).margin({ top: 15 })
+ }.width('100%').height(60)
+
+ Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
+ Button("测试融合动画")
+ .onClick(() => {
+ router.pushUrl({ url: "pages/VAPXAnimation" });
+ }).margin({ top: 15 })
+ }.width('100%').height(60)
+ }.backgroundColor(Color.Pink)
+ .margin(10)
+ .padding(20)
+ .borderRadius(10)
+ }
+ .width('100%')
+ .height('100%')
+ }
+}
diff --git a/ohos/entry/src/main/ets/pages/VAPXAnimation.ets b/ohos/entry/src/main/ets/pages/VAPXAnimation.ets
new file mode 100644
index 00000000..fc6a3f0f
--- /dev/null
+++ b/ohos/entry/src/main/ets/pages/VAPXAnimation.ets
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { MixData, VAPPlayer, VAPColor } from 'vap_module';
+import { systemDateTime } from '@kit.BasicServicesKit';
+import { LogUtil } from '../common/LogUtil';
+import { util } from '@kit.ArkTS';
+import { FitType } from 'vap_module';
+import { VAPFontWeight } from 'vap_module';
+import { VAPTextAlign } from 'vap_module';
+import { SrcInfo, SrcType } from 'vap_module';
+import text from '@ohos.graphics.text';
+
+class ColorBean implements VAPColor {
+ a: number = 255;
+ r: number = 2;
+ g: number = 255;
+ b: number = 187
+}
+
+@Entry
+@Component
+struct VAPXAnimation {
+ private vapPlayer: VAPPlayer | undefined = undefined;
+ @State buttonEnabled: boolean = true;
+ @State fitType: FitType = FitType.FIT_CENTER;
+ @State textAlign: VAPTextAlign = VAPTextAlign.TEXT_ALIGN_LEFT;
+ @State fontWeight: VAPFontWeight = VAPFontWeight.NORMAL;
+ @State color: ColorBean = new ColorBean;
+ @State showColor: number = 0;
+ private srcInfo: SrcInfo[] | undefined = undefined
+ private playTimes: number = 0;
+ private stopTimes: number = 0;
+ private pauseTimes: number = 0;
+ private colorIdx: number = 0
+
+ @Styles tipTxt() {
+ .backgroundColor('#fff')
+ .foregroundColor('#000')
+ }
+ aboutToDisappear(): void {
+ LogUtil.info('aboutToDisappear')
+ }
+
+ onPageShow(): void {
+ LogUtil.info('onPageShow')
+ this.showColor = (((this.color.r & 0xff) << 16) | ((this.color.g & 0xff) << 8) | ((this.color.b & 0xff)))
+ }
+
+ onPageHide(): void {
+ LogUtil.info('onPageHide')
+ this.vapPlayer?.stop()
+ }
+
+ play() {
+ if (this.vapPlayer === undefined) {
+ LogUtil.error('plz create XComponent first')
+ return
+ }
+ this.vapPlayer?.setFitType(this.fitType)
+ try {
+ if (this.srcInfo) {
+ let mixData: Array = []
+ this.srcInfo.map((ele, idx) => {
+ if (ele.type == SrcType.IMG) {
+ mixData.push({ tag: ele.tag, imgUri: '图片地址' })
+ } else if (ele.type == SrcType.TXT) {
+ mixData.push({ tag: ele.tag, txt: '文本信息', color: this.color })
+ }
+ })
+ console.log('config mixData: ' + JSON.stringify(mixData))
+ } else {
+ console.log('error not get srcInfo')
+ }
+ let opts: Array = [{
+ tag: 'sImg1',
+ imgUri: getContext(this).filesDir + '/head1.png'
+ }, {
+ tag: 'abc',
+ txt: "星河Harmony NEXT",
+ imgUri: getContext(this).filesDir + '/head1.png'
+ }, {
+ tag: 'sTxt1',
+ txt: "星河Harmony NEXT",
+ textAlign: this.textAlign,
+ fontWeight: this.fontWeight,
+ color: this.color
+ }];
+ this.buttonEnabled = false;
+
+ let startTime = systemDateTime.getTime(true)
+
+ this.vapPlayer?.play(getContext(this).filesDir + "/vapx.mp4", opts, () => {
+ LogUtil.info("js get callback")
+ this.buttonEnabled = true;
+ });
+ let endTime = systemDateTime.getTime(true)
+ LogUtil.info(this.playTimes + " play cost " + (endTime - startTime) / 1000)
+ } catch (e) {
+ LogUtil.error(this.playTimes + " play error " + JSON.stringify(e));
+ }
+ this.playTimes++;
+ }
+
+ pause() {
+ try {
+ let startTime = systemDateTime.getTime(true);
+ this.vapPlayer?.pause();
+ let endTime = systemDateTime.getTime(true)
+ LogUtil.info(this.pauseTimes + " pause cost " + (endTime - startTime) / 1000)
+ } catch (e) {
+ LogUtil.error(this.pauseTimes + " pause error " + JSON.stringify(e));
+ }
+ this.pauseTimes++;
+ }
+
+ stop() {
+ try {
+ let startTime = systemDateTime.getTime(true);
+ this.vapPlayer?.stop();
+ let endTime = systemDateTime.getTime(true)
+ LogUtil.info(this.stopTimes + " stop cost " + (endTime - startTime) / 1000)
+ } catch (e) {
+ LogUtil.error(this.stopTimes + " stop error " + JSON.stringify(e));
+ }
+ this.stopTimes++;
+ }
+
+ build() {
+ Column() {
+ Stack() {
+ XComponent({
+ id: 'xcomponentId_' + util.generateRandomUUID(),
+ type: 'surface',
+ libraryname: 'vap'
+ })
+ .onLoad((context?: object) => {
+ if (context) {
+ this.vapPlayer = new VAPPlayer;
+ this.vapPlayer.setContext(context);
+ let info = this.vapPlayer.getVideoInfo(getContext(this).filesDir + "/vapx.mp4")
+ console.log('getVideoInfo info ' + JSON.stringify(info))
+ this.srcInfo = info?.srcInfos
+ }
+ })
+ .backgroundColor(Color.Transparent)
+ .width('100%')
+ .height('100%')
+ .visibility(this.buttonEnabled ? Visibility.Hidden : Visibility.Visible)
+
+ Scroll() {
+ Row({ space: 10 }) {
+ Text('色彩分量:').fontWeight(FontWeight.Bold)
+ Select([{ value: 'red' }, { value: 'green' }, { value: 'blue' }])
+ .selected(0)
+ .value('red')
+ .onSelect((index: number) => {
+ this.colorIdx = index
+ })
+ TextInput()
+ .type(InputType.Number)
+ .width(80)
+ .maxLength(3)
+ .onChange((value: string) => {
+ if (this.colorIdx == 0) {
+ this.color.r = Number(value) & 0xff
+ } else if (this.colorIdx == 1) {
+ this.color.g = Number(value) & 0xff
+ } else if (this.colorIdx == 2) {
+ this.color.b = Number(value) & 0xff
+ }
+ this.showColor =
+ (((this.color.r & 0xff) << 16) | ((this.color.g & 0xff) << 8) | ((this.color.b & 0xff)))
+ })
+ Text('当前色彩:').tipTxt().fontWeight(FontWeight.Bold)
+ Text().width(20).height(20).backgroundColor(this.showColor)
+
+ Text('视频对齐:').fontWeight(FontWeight.Bold)
+ Select([{ value: 'fix_xy' }, { value: 'fix_center' }, { value: 'center_crop' }])
+ .selected(1)
+ .value('fix_center')
+ .onSelect((index: number) => {
+ this.fitType = index
+ })
+ Text('字体权重:').fontWeight(FontWeight.Bold)
+ Select([{ value: 'thin' }, { value: 'extra_light' }, { value: 'light' }, { value: 'normal' },
+ { value: 'medium' }, { value: 'semi_bold' },
+ { value: 'bold' }, { value: 'extra_bold' }, { value: 'black' }])
+ .selected(3)
+ .value('normal')
+ .onSelect((index: number) => {
+ this.fontWeight = index
+ })
+ Text('字体对齐:').fontWeight(FontWeight.Bold)
+ Select([{ value: 'left' }, { value: 'right' }, { value: 'center' }, { value: 'justify' },
+ { value: 'start' }, { value: 'end' }])
+ .selected(0)
+ .value('left')
+ .onSelect((index: number) => {
+ this.textAlign = index
+ })
+ }
+ }
+ .width('100%')
+ .scrollable(ScrollDirection.Horizontal)
+ .padding({ bottom: 10 })
+ .margin({ left: 5, right: 5 })
+ .border({
+ color: Color.Grey,
+ style: BorderStyle.Solid,
+ radius: 5,
+ width: 2
+ })
+ .backgroundColor(Color.White)
+ .opacity(0.6)
+ }
+ .backgroundImage($r('app.media.bg'))
+ .backgroundImageSize(ImageSize.FILL)
+ .height('93%')
+ .width('100%')
+ .alignContent(Alignment.Bottom)
+
+ Row() {
+ Button('Play')
+ .fontWeight(500)
+ .onClick(() => {
+ this.play()
+ })
+ .width('200')
+ .enabled(this.buttonEnabled)
+ Button('pause')
+ .onClick(() => {
+ this.pause()
+ })
+ Button('stop')
+ .onClick(() => {
+ this.stop()
+ })
+ }
+ .margin({ top: 15 })
+ .width('100%')
+ .justifyContent(FlexAlign.SpaceAround)
+ .alignItems(VerticalAlign.Bottom)
+ }
+ }
+}
\ No newline at end of file
diff --git a/ohos/entry/src/main/module.json5 b/ohos/entry/src/main/module.json5
new file mode 100644
index 00000000..59041a9a
--- /dev/null
+++ b/ohos/entry/src/main/module.json5
@@ -0,0 +1,62 @@
+{
+ "module": {
+ "name": "entry",
+ "type": "entry",
+ "description": "$string:module_desc",
+ "mainElement": "EntryAbility",
+ "deviceTypes": [
+ "default",
+ "tablet"
+ ],
+ "deliveryWithInstall": true,
+ "installationFree": false,
+ "pages": "$profile:main_pages",
+ "abilities": [
+ {
+ "name": "EntryAbility",
+ "srcEntry": "./ets/entryability/EntryAbility.ts",
+ "description": "$string:EntryAbility_desc",
+ "icon": "$media:icon",
+ "label": "$string:EntryAbility_label",
+ "startWindowIcon": "$media:startIcon",
+ "startWindowBackground": "$color:start_window_background",
+ "exported": true,
+ "skills": [
+ {
+ "entities": [
+ "entity.system.home"
+ ],
+ "actions": [
+ "action.system.home"
+ ]
+ }
+ ]
+ }
+ ],
+ "requestPermissions": [
+ {
+ "name": 'ohos.permission.READ_MEDIA',
+ "reason": '$string:read_file',
+ "usedScene": {
+ "abilities": [
+ "EntryAbility"
+ ],
+ "when": "always"
+ }
+ },
+ {
+ "name": 'ohos.permission.WRITE_MEDIA',
+ "reason": '$string:read_file',
+ "usedScene": {
+ "abilities": [
+ "EntryAbility"
+ ],
+ "when": "always"
+ }
+ },
+ {
+ "name": "ohos.permission.INTERNET"
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/ohos/entry/src/main/resources/base/element/color.json b/ohos/entry/src/main/resources/base/element/color.json
new file mode 100644
index 00000000..35b1de4b
--- /dev/null
+++ b/ohos/entry/src/main/resources/base/element/color.json
@@ -0,0 +1,12 @@
+{
+ "color": [
+ {
+ "name": "start_window_background",
+ "value": "#FFFFFF"
+ },
+ {
+ "name": "first",
+ "value": "#92D6CC"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/ohos/entry/src/main/resources/base/element/float.json b/ohos/entry/src/main/resources/base/element/float.json
new file mode 100644
index 00000000..5f86e5b9
--- /dev/null
+++ b/ohos/entry/src/main/resources/base/element/float.json
@@ -0,0 +1,48 @@
+{
+ "float": [
+ {
+ "name": "title_text_font_size",
+ "value": "24fp"
+ },
+ {
+ "name": "title_text_margin_left",
+ "value": "24vp"
+ },
+ {
+ "name": "title_text_margin_top",
+ "value": "12vp"
+ },
+ {
+ "name": "title_margin_top",
+ "value": "24vp"
+ },
+ {
+ "name": "title_height",
+ "value": "56vp"
+ },
+ {
+ "name": "xcomponent_margin_top",
+ "value": "27vp"
+ },
+ {
+ "name": "xcomponent_margin_left",
+ "value": "12vp"
+ },
+ {
+ "name": "xcomponent_margin_right",
+ "value": "12vp"
+ },
+ {
+ "name": "button_font_size",
+ "value": "16fp"
+ },
+ {
+ "name": "button_margin_bottom",
+ "value": "24vp"
+ },
+ {
+ "name": "button_height",
+ "value": "40vp"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/ohos/entry/src/main/resources/base/element/string.json b/ohos/entry/src/main/resources/base/element/string.json
new file mode 100644
index 00000000..877aaf61
--- /dev/null
+++ b/ohos/entry/src/main/resources/base/element/string.json
@@ -0,0 +1,28 @@
+{
+ "string": [
+ {
+ "name": "module_desc",
+ "value": "module description"
+ },
+ {
+ "name": "EntryAbility_desc",
+ "value": "description"
+ },
+ {
+ "name": "EntryAbility_label",
+ "value": "XComponent"
+ },
+ {
+ "name": "title",
+ "value": "Render Example"
+ },
+ {
+ "name": "button_text",
+ "value": "LoadYUV"
+ },
+ {
+ "name": "read_file",
+ "value": "文件读取"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/ohos/entry/src/main/resources/base/media/bg.png b/ohos/entry/src/main/resources/base/media/bg.png
new file mode 100644
index 00000000..9172073c
Binary files /dev/null and b/ohos/entry/src/main/resources/base/media/bg.png differ
diff --git a/ohos/entry/src/main/resources/base/media/coollb.png b/ohos/entry/src/main/resources/base/media/coollb.png
new file mode 100644
index 00000000..bc9db6fd
Binary files /dev/null and b/ohos/entry/src/main/resources/base/media/coollb.png differ
diff --git a/ohos/entry/src/main/resources/base/media/icon.png b/ohos/entry/src/main/resources/base/media/icon.png
new file mode 100644
index 00000000..ce307a88
Binary files /dev/null and b/ohos/entry/src/main/resources/base/media/icon.png differ
diff --git a/ohos/entry/src/main/resources/base/media/startIcon.png b/ohos/entry/src/main/resources/base/media/startIcon.png
new file mode 100644
index 00000000..95b3d6ba
Binary files /dev/null and b/ohos/entry/src/main/resources/base/media/startIcon.png differ
diff --git a/ohos/entry/src/main/resources/base/profile/main_pages.json b/ohos/entry/src/main/resources/base/profile/main_pages.json
new file mode 100644
index 00000000..21955cb5
--- /dev/null
+++ b/ohos/entry/src/main/resources/base/profile/main_pages.json
@@ -0,0 +1,7 @@
+{
+ "src": [
+ "pages/Index",
+ "pages/GeneralAnimation",
+ "pages/VAPXAnimation"
+ ]
+}
diff --git a/ohos/entry/src/main/resources/en_US/element/string.json b/ohos/entry/src/main/resources/en_US/element/string.json
new file mode 100644
index 00000000..6b976fb6
--- /dev/null
+++ b/ohos/entry/src/main/resources/en_US/element/string.json
@@ -0,0 +1,16 @@
+{
+ "string": [
+ {
+ "name": "module_desc",
+ "value": "module description"
+ },
+ {
+ "name": "EntryAbility_desc",
+ "value": "description"
+ },
+ {
+ "name": "EntryAbility_label",
+ "value": "label"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/ohos/entry/src/main/resources/rawfile/demo.mp4 b/ohos/entry/src/main/resources/rawfile/demo.mp4
new file mode 100644
index 00000000..3c12bf31
Binary files /dev/null and b/ohos/entry/src/main/resources/rawfile/demo.mp4 differ
diff --git a/ohos/entry/src/main/resources/rawfile/great_gift.mp4 b/ohos/entry/src/main/resources/rawfile/great_gift.mp4
new file mode 100644
index 00000000..bbbcf0e9
Binary files /dev/null and b/ohos/entry/src/main/resources/rawfile/great_gift.mp4 differ
diff --git a/ohos/entry/src/main/resources/rawfile/head1.png b/ohos/entry/src/main/resources/rawfile/head1.png
new file mode 100644
index 00000000..1b24cebf
Binary files /dev/null and b/ohos/entry/src/main/resources/rawfile/head1.png differ
diff --git a/ohos/entry/src/main/resources/rawfile/vapx.mp4 b/ohos/entry/src/main/resources/rawfile/vapx.mp4
new file mode 100644
index 00000000..76f39230
Binary files /dev/null and b/ohos/entry/src/main/resources/rawfile/vapx.mp4 differ
diff --git a/ohos/entry/src/main/resources/zh_CN/element/string.json b/ohos/entry/src/main/resources/zh_CN/element/string.json
new file mode 100644
index 00000000..f531f2a2
--- /dev/null
+++ b/ohos/entry/src/main/resources/zh_CN/element/string.json
@@ -0,0 +1,16 @@
+{
+ "string": [
+ {
+ "name": "module_desc",
+ "value": "模块描述"
+ },
+ {
+ "name": "EntryAbility_desc",
+ "value": "description"
+ },
+ {
+ "name": "EntryAbility_label",
+ "value": "vap"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/ohos/entry/src/ohosTest/ets/test/Ability.test.ets b/ohos/entry/src/ohosTest/ets/test/Ability.test.ets
new file mode 100644
index 00000000..9fc3234e
--- /dev/null
+++ b/ohos/entry/src/ohosTest/ets/test/Ability.test.ets
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import hilog from '@ohos.hilog';
+import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';
+
+export default function abilityTest() {
+ describe('ActsAbilityTest', () => {
+ // Defines a test suite. Two parameters are supported: test suite name and test suite function.
+ beforeAll(() => {
+ // Presets an action, which is performed only once before all test cases of the test suite start.
+ // This API supports only one parameter: preset action function.
+ })
+ beforeEach(() => {
+ // Presets an action, which is performed before each unit test case starts.
+ // The number of execution times is the same as the number of test cases defined by **it**.
+ // This API supports only one parameter: preset action function.
+ })
+ afterEach(() => {
+ // Presets a clear action, which is performed after each unit test case ends.
+ // The number of execution times is the same as the number of test cases defined by **it**.
+ // This API supports only one parameter: clear action function.
+ })
+ afterAll(() => {
+ // Presets a clear action, which is performed after all test cases of the test suite end.
+ // This API supports only one parameter: clear action function.
+ })
+ it('assertContain', 0, () => {
+ // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
+ hilog.info(0x0000, 'testTag', '%{public}s', 'it begin');
+ let a = 'abc';
+ let b = 'b';
+ // Defines a variety of assertion methods, which are used to declare expected boolean conditions.
+ expect(a).assertContain(b);
+ expect(a).assertEqual(a);
+ })
+ })
+}
\ No newline at end of file
diff --git a/ohos/entry/src/ohosTest/ets/test/List.test.ets b/ohos/entry/src/ohosTest/ets/test/List.test.ets
new file mode 100644
index 00000000..f11eed8c
--- /dev/null
+++ b/ohos/entry/src/ohosTest/ets/test/List.test.ets
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import abilityTest from './Ability.test';
+
+export default function testsuite() {
+ abilityTest();
+}
\ No newline at end of file
diff --git a/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets b/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets
new file mode 100644
index 00000000..88adef8b
--- /dev/null
+++ b/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import UIAbility from '@ohos.app.ability.UIAbility';
+import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';
+import hilog from '@ohos.hilog';
+import { Hypium } from '@ohos/hypium';
+import testsuite from '../test/List.test';
+import window from '@ohos.window';
+import Want from '@ohos.app.ability.Want';
+import AbilityConstant from '@ohos.app.ability.AbilityConstant';
+
+export default class TestAbility extends UIAbility {
+ onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onCreate');
+ hilog.info(0x0000, 'testTag', '%{public}s', 'want param:' + JSON.stringify(want) ?? '');
+ hilog.info(0x0000, 'testTag', '%{public}s', 'launchParam:' + JSON.stringify(launchParam) ?? '');
+ let abilityDelegator: AbilityDelegatorRegistry.AbilityDelegator;
+ abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator();
+ let abilityDelegatorArguments: AbilityDelegatorRegistry.AbilityDelegatorArgs;
+ abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments();
+ hilog.info(0x0000, 'testTag', '%{public}s', 'start run testcase!!!');
+ Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite);
+ }
+
+ onDestroy() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onDestroy');
+ }
+
+ onWindowStageCreate(windowStage: window.WindowStage) {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageCreate');
+ windowStage.loadContent('testability/pages/Index', (err, data) => {
+ if (err.code) {
+ hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
+ return;
+ }
+ hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s',
+ JSON.stringify(data) ?? '');
+ });
+ }
+
+ onWindowStageDestroy() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageDestroy');
+ }
+
+ onForeground() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onForeground');
+ }
+
+ onBackground() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onBackground');
+ }
+}
\ No newline at end of file
diff --git a/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets b/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets
new file mode 100644
index 00000000..a06262f1
--- /dev/null
+++ b/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@Entry
+@Component
+struct Index {
+ @State message: string = 'Hello World';
+
+ build() {
+ Row() {
+ Column() {
+ Text(this.message)
+ .fontSize(50)
+ .fontWeight(FontWeight.Bold)
+ }
+ .width('100%')
+ }
+ .height('100%')
+ }
+}
\ No newline at end of file
diff --git a/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ets b/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ets
new file mode 100644
index 00000000..3c73e6f8
--- /dev/null
+++ b/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ets
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import hilog from '@ohos.hilog';
+import TestRunner from '@ohos.application.testRunner';
+import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';
+import Want from '@ohos.app.ability.Want';
+
+let abilityDelegator: AbilityDelegatorRegistry.AbilityDelegator | undefined = undefined
+let abilityDelegatorArguments: AbilityDelegatorRegistry.AbilityDelegatorArgs | undefined = undefined
+
+async function onAbilityCreateCallback() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'onAbilityCreateCallback');
+}
+
+async function addAbilityMonitorCallback(err : Error) {
+ hilog.info(0x0000, 'testTag', 'addAbilityMonitorCallback : %{public}s', JSON.stringify(err) ?? '');
+}
+
+export default class OpenHarmonyTestRunner implements TestRunner {
+ constructor() {
+ }
+
+ onPrepare() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner OnPrepare ');
+ }
+
+ async onRun() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun run');
+ abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments()
+ abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()
+ const bundleName = abilityDelegatorArguments.bundleName;
+ const testAbilityName = 'TestAbility';
+ let lMonitor: AbilityDelegatorRegistry.AbilityMonitor = {
+ abilityName: testAbilityName,
+ onAbilityCreate: onAbilityCreateCallback,
+ };
+ abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback)
+ const want: Want = {
+ bundleName: bundleName,
+ abilityName: testAbilityName
+ };
+ abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator();
+ abilityDelegator.startAbility(want, (err, data) => {
+ hilog.info(0x0000, 'testTag', 'startAbility : err : %{public}s', JSON.stringify(err) ?? '');
+ hilog.info(0x0000, 'testTag', 'startAbility : data : %{public}s',JSON.stringify(data) ?? '');
+ })
+ hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun end');
+ }
+}
\ No newline at end of file
diff --git a/ohos/entry/src/ohosTest/module.json5 b/ohos/entry/src/ohosTest/module.json5
new file mode 100644
index 00000000..82ff674b
--- /dev/null
+++ b/ohos/entry/src/ohosTest/module.json5
@@ -0,0 +1,37 @@
+{
+ "module": {
+ "name": "entry_test",
+ "type": "feature",
+ "description": "$string:module_test_desc",
+ "mainElement": "TestAbility",
+ "deviceTypes": [
+ "default",
+ "tablet"
+ ],
+ "deliveryWithInstall": true,
+ "installationFree": false,
+ "pages": "$profile:test_pages",
+ "abilities": [
+ {
+ "name": "TestAbility",
+ "srcEntry": "./ets/testability/TestAbility.ets",
+ "description": "$string:TestAbility_desc",
+ "icon": "$media:icon",
+ "label": "$string:TestAbility_label",
+ "exported": true,
+ "startWindowIcon": "$media:icon",
+ "startWindowBackground": "$color:start_window_background",
+ "skills": [
+ {
+ "actions": [
+ "action.system.home"
+ ],
+ "entities": [
+ "entity.system.home"
+ ]
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/ohos/entry/src/ohosTest/resources/base/element/color.json b/ohos/entry/src/ohosTest/resources/base/element/color.json
new file mode 100644
index 00000000..d66f9a7d
--- /dev/null
+++ b/ohos/entry/src/ohosTest/resources/base/element/color.json
@@ -0,0 +1,8 @@
+{
+ "color": [
+ {
+ "name": "start_window_background",
+ "value": "#FFFFFF"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/ohos/entry/src/ohosTest/resources/base/element/string.json b/ohos/entry/src/ohosTest/resources/base/element/string.json
new file mode 100644
index 00000000..04e87abb
--- /dev/null
+++ b/ohos/entry/src/ohosTest/resources/base/element/string.json
@@ -0,0 +1,16 @@
+{
+ "string": [
+ {
+ "name": "module_test_desc",
+ "value": "test ability description"
+ },
+ {
+ "name": "TestAbility_desc",
+ "value": "the test ability"
+ },
+ {
+ "name": "TestAbility_label",
+ "value": "test label"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/ohos/entry/src/ohosTest/resources/base/media/icon.png b/ohos/entry/src/ohosTest/resources/base/media/icon.png
new file mode 100644
index 00000000..ce307a88
Binary files /dev/null and b/ohos/entry/src/ohosTest/resources/base/media/icon.png differ
diff --git a/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json b/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json
new file mode 100644
index 00000000..c3d813c4
--- /dev/null
+++ b/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json
@@ -0,0 +1,5 @@
+{
+ "src": [
+ "testability/pages/Index"
+ ]
+}
diff --git a/ohos/entry/src/test/List.test.ets b/ohos/entry/src/test/List.test.ets
new file mode 100644
index 00000000..113768a8
--- /dev/null
+++ b/ohos/entry/src/test/List.test.ets
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import localUnitTest from './LocalUnit.test';
+
+export default function testsuite() {
+ localUnitTest();
+}
\ No newline at end of file
diff --git a/ohos/entry/src/test/LocalUnit.test.ets b/ohos/entry/src/test/LocalUnit.test.ets
new file mode 100644
index 00000000..60a6cf0e
--- /dev/null
+++ b/ohos/entry/src/test/LocalUnit.test.ets
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';
+
+export default function localUnitTest() {
+ describe('localUnitTest',() => {
+ // Defines a test suite. Two parameters are supported: test suite name and test suite function.
+ beforeAll(() => {
+ // Presets an action, which is performed only once before all test cases of the test suite start.
+ // This API supports only one parameter: preset action function.
+ });
+ beforeEach(() => {
+ // Presets an action, which is performed before each unit test case starts.
+ // The number of execution times is the same as the number of test cases defined by **it**.
+ // This API supports only one parameter: preset action function.
+ });
+ afterEach(() => {
+ // Presets a clear action, which is performed after each unit test case ends.
+ // The number of execution times is the same as the number of test cases defined by **it**.
+ // This API supports only one parameter: clear action function.
+ });
+ afterAll(() => {
+ // Presets a clear action, which is performed after all test cases of the test suite end.
+ // This API supports only one parameter: clear action function.
+ });
+ it('assertContain', 0, () => {
+ // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
+ let a = 'abc';
+ let b = 'b';
+ // Defines a variety of assertion methods, which are used to declare expected boolean conditions.
+ expect(a).assertContain(b);
+ expect(a).assertEqual(a);
+ });
+ });
+}
\ No newline at end of file
diff --git a/ohos/hvigor/hvigor-config.json5 b/ohos/hvigor/hvigor-config.json5
new file mode 100644
index 00000000..64d4de8d
--- /dev/null
+++ b/ohos/hvigor/hvigor-config.json5
@@ -0,0 +1,17 @@
+{
+ "modelVersion": "5.0.0",
+ "dependencies": {
+ },
+ "execution": {
+ // "daemon": true, /* Enable daemon compilation. Default: true */
+ // "incremental": true, /* Enable incremental compilation. Default: true */
+ // "parallel": true, /* Enable parallel compilation. Default: true */
+ // "typeCheck": false, /* Enable typeCheck. Default: false */
+ },
+ "logging": {
+ // "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */
+ },
+ "debugging": {
+ // "stacktrace": false /* Disable stacktrace compilation. Default: false */
+ }
+}
\ No newline at end of file
diff --git a/ohos/hvigorfile.ts b/ohos/hvigorfile.ts
new file mode 100644
index 00000000..f3cb9f1a
--- /dev/null
+++ b/ohos/hvigorfile.ts
@@ -0,0 +1,6 @@
+import { appTasks } from '@ohos/hvigor-ohos-plugin';
+
+export default {
+ system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
+ plugins:[] /* Custom plugin to extend the functionality of Hvigor. */
+}
diff --git a/ohos/imgs/1.gif b/ohos/imgs/1.gif
new file mode 100644
index 00000000..f522d74d
Binary files /dev/null and b/ohos/imgs/1.gif differ
diff --git a/ohos/imgs/2.gif b/ohos/imgs/2.gif
new file mode 100644
index 00000000..a0009190
Binary files /dev/null and b/ohos/imgs/2.gif differ
diff --git a/ohos/imgs/4.gif b/ohos/imgs/4.gif
new file mode 100644
index 00000000..2f4c61ce
Binary files /dev/null and b/ohos/imgs/4.gif differ
diff --git a/ohos/imgs/5.gif b/ohos/imgs/5.gif
new file mode 100644
index 00000000..ddbe60dd
Binary files /dev/null and b/ohos/imgs/5.gif differ
diff --git a/ohos/imgs/crop.png b/ohos/imgs/crop.png
new file mode 100644
index 00000000..bd7abbbc
Binary files /dev/null and b/ohos/imgs/crop.png differ
diff --git a/ohos/imgs/icon.png b/ohos/imgs/icon.png
new file mode 100644
index 00000000..c8748024
Binary files /dev/null and b/ohos/imgs/icon.png differ
diff --git a/ohos/oh-package-lock.json5 b/ohos/oh-package-lock.json5
new file mode 100644
index 00000000..eefd5bce
--- /dev/null
+++ b/ohos/oh-package-lock.json5
@@ -0,0 +1,20 @@
+{
+ "meta": {
+ "stableOrder": true
+ },
+ "lockfileVersion": 3,
+ "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.",
+ "specifiers": {
+ "@ohos/hypium@1.0.18": "@ohos/hypium@1.0.18"
+ },
+ "packages": {
+ "@ohos/hypium@1.0.18": {
+ "name": "@ohos/hypium",
+ "version": "1.0.18",
+ "integrity": "sha512-UMwbHlaJ1lEaJkDoQC/NH8XztcKysKWjzwgybDHULNKP8undtPtJbFww69GhTp8EG6+NO1uXimRBUl6sBI2nWw==",
+ "resolved": "https://cmc.centralrepo.rnd.huawei.com/artifactory/api/npm/product_npm/@ohos/hypium/-/@ohos/hypium-1.0.18.tgz",
+ "shasum": "cdbaf30436c45461a0c527da1aab0e916c7e1195",
+ "registryType": "npm"
+ }
+ }
+}
\ No newline at end of file
diff --git a/ohos/oh-package.json5 b/ohos/oh-package.json5
new file mode 100644
index 00000000..8efe922e
--- /dev/null
+++ b/ohos/oh-package.json5
@@ -0,0 +1,8 @@
+{
+ "modelVersion": "5.0.0",
+ "description": "Please describe the basic information.",
+ "dependencies": {},
+ "devDependencies": {
+ "@ohos/hypium": "1.0.18",
+ }
+}
\ No newline at end of file
diff --git a/ohos/vap_module/.gitignore b/ohos/vap_module/.gitignore
new file mode 100644
index 00000000..32abf8fd
--- /dev/null
+++ b/ohos/vap_module/.gitignore
@@ -0,0 +1,6 @@
+/node_modules
+/oh_modules
+/.preview
+/build
+/.cxx
+/.test
\ No newline at end of file
diff --git a/ohos/vap_module/BuildProfile.ets b/ohos/vap_module/BuildProfile.ets
new file mode 100644
index 00000000..3a501e5d
--- /dev/null
+++ b/ohos/vap_module/BuildProfile.ets
@@ -0,0 +1,17 @@
+/**
+ * Use these variables when you tailor your ArkTS code. They must be of the const type.
+ */
+export const HAR_VERSION = '1.0.0';
+export const BUILD_MODE_NAME = 'debug';
+export const DEBUG = true;
+export const TARGET_NAME = 'default';
+
+/**
+ * BuildProfile Class is used only for compatibility purposes.
+ */
+export default class BuildProfile {
+ static readonly HAR_VERSION = HAR_VERSION;
+ static readonly BUILD_MODE_NAME = BUILD_MODE_NAME;
+ static readonly DEBUG = DEBUG;
+ static readonly TARGET_NAME = TARGET_NAME;
+}
\ No newline at end of file
diff --git a/ohos/vap_module/HttpReq.ets b/ohos/vap_module/HttpReq.ets
new file mode 100644
index 00000000..7bfa75c3
--- /dev/null
+++ b/ohos/vap_module/HttpReq.ets
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { http } from '@kit.NetworkKit';
+import { AsyncCallback, BusinessError } from '@kit.BasicServicesKit';
+import fs from '@ohos.file.fs';
+
+function extractFileName(url: string): string {
+ // 使用正则表达式匹配 URL 中的文件名部分
+ const matches = url.match(/\/([^\/?#]+)[^\/]*$/);
+ if (matches && matches.length > 1) {
+ return matches[1];
+ }
+ // 如果没有匹配到,则返回空字符串或者你想要的默认值
+ return '';
+}
+
+function startsWithHttp(url: string): boolean {
+ return url.startsWith("http");
+}
+
+function writeFile(filePath: string, buf: ArrayBuffer) {
+ let file = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
+ fs.writeSync(file.fd, buf);
+ fs.closeSync(file);
+}
+
+export async function downloadFile(url: string, dir?: string) {
+ console.log('enter downloadFile: ' + JSON.stringify(url));
+ if(!startsWithHttp(url)) {
+ return url;
+ }
+ const fileName = extractFileName(url);
+ let filePath = '/storage/Users/currentUser/Documents/' + fileName
+ if(dir) {
+ filePath = dir + '/' + fileName
+ }
+ let res = fs.accessSync(filePath);
+ if (res) {
+ return filePath;
+ }
+
+ let httpRequest = http.createHttp();
+ try {
+ let data = await httpRequest.request(url, {
+ method: http.RequestMethod.GET,
+ expectDataType: http.HttpDataType.ARRAY_BUFFER,
+ })
+ let buf:ArrayBuffer = data.result as ArrayBuffer;
+ writeFile(filePath, buf)
+ } catch (err) {
+ console.error("accessSync failed with error message: " + err.message + ", error code: " + err.code);
+ filePath = ''
+ } finally {
+ httpRequest.destroy();
+ }
+ return filePath
+ // let promise:Promise = new Promise((resolve: Function, reject: Function) => {
+ // let httpRequest = http.createHttp();
+ // httpRequest.request(url, {
+ // method: http.RequestMethod.GET,
+ // expectDataType: http.HttpDataType.ARRAY_BUFFER,
+ // }).then((data) => {
+ // let buf:ArrayBuffer = data.result as ArrayBuffer;
+ // writeFile(filePath, buf)
+ // resolve(filePath);
+ // }).catch((err: BusinessError) => {
+ // console.error("accessSync failed with error message: " + err.message + ", error code: " + err.code);
+ // reject(err)
+ // }).finally(()=>{
+ // httpRequest.destroy();
+ // })
+ // })
+ // let result: string = await promise
+ // return result;
+}
+
+
+export function downMediaFile(url:string, callback: AsyncCallback) {
+ if(undefined == callback) {
+ return
+ }
+ if(!startsWithHttp(url)) {
+ callback(undefined, url);
+ }
+ let httpRequest = http.createHttp();
+ const fileName = extractFileName(url);
+ console.error('fileName:' + JSON.stringify(fileName));
+ let filePath = '/storage/Users/currentUser/Documents/' + fileName
+
+ try {
+ let res = fs.accessSync(filePath);
+ if (res) {
+ callback(undefined, filePath);
+ } else {
+ httpRequest.on('headersReceive', (header) => {
+ console.info('header: ' + JSON.stringify(header));
+ });
+ httpRequest.request(
+ // 填写HTTP请求的URL地址,可以带参数也可以不带参数。URL地址需要开发者自定义。请求的参数可以在extraData中指定
+ url,
+ {
+ method: http.RequestMethod.GET, // 可选,默认为http.RequestMethod.GET
+ // 开发者根据自身业务需要添加header字段
+ // header: {
+ // 'Content-Type': 'video/mp4'
+ // },
+ expectDataType: http.HttpDataType.ARRAY_BUFFER, // 可选,指定返回数据的类型
+ }, (err: BusinessError, data: http.HttpResponse) => {
+ if (!err) {
+ let buf:ArrayBuffer = data.result as ArrayBuffer;
+
+ console.info('Result:' + JSON.stringify(buf.byteLength));
+ console.info('resultType:' + JSON.stringify(data.resultType));
+ console.info('code:' + JSON.stringify(data.responseCode));
+ // data.header为HTTP响应头,可根据业务需要进行解析
+ console.info('header:' + JSON.stringify(data.header));
+ console.info('cookies:' + JSON.stringify(data.cookies)); // 8+
+ let file = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
+ fs.writeSync(file.fd, buf);
+ fs.closeSync(file);
+ callback(undefined, filePath);
+ // 当该请求使用完毕时,调用destroy方法主动销毁
+ httpRequest.destroy();
+ } else {
+ console.error('error:' + JSON.stringify(err));
+ // 取消订阅HTTP响应头事件
+ httpRequest.off('headersReceive');
+ callback(err, '');
+ // 当该请求使用完毕时,调用destroy方法主动销毁
+ httpRequest.destroy();
+ }
+ });
+ console.info("file not exists");
+ }
+ } catch(error) {
+ let err: BusinessError = error as BusinessError;
+ console.error("accessSync failed with error message: " + err.message + ", error code: " + err.code);
+ }
+}
diff --git a/ohos/vap_module/Index.ets b/ohos/vap_module/Index.ets
new file mode 100644
index 00000000..bfaebda6
--- /dev/null
+++ b/ohos/vap_module/Index.ets
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export * from './src/main/ets/VAPPlayer'
+export * from './src/main/ets/Index'
+export * from './src/main/ets/DataDeclare'
diff --git a/ohos/vap_module/build-profile.json5 b/ohos/vap_module/build-profile.json5
new file mode 100644
index 00000000..66f30376
--- /dev/null
+++ b/ohos/vap_module/build-profile.json5
@@ -0,0 +1,22 @@
+{
+ "apiType": "stageMode",
+ "buildOption": {
+ "externalNativeOptions": {
+ "path": "./src/main/cpp/CMakeLists.txt",
+ "arguments": "",
+ "abiFilters": [
+ "arm64-v8a",
+ "x86_64",
+ ],
+ "cppFlags": ""
+ },
+ },
+ "targets": [
+ {
+ "name": "default"
+ },
+ {
+ "name": "ohosTest"
+ }
+ ]
+}
diff --git a/ohos/vap_module/consumer-rules.txt b/ohos/vap_module/consumer-rules.txt
new file mode 100644
index 00000000..e69de29b
diff --git a/ohos/vap_module/hvigorfile.ts b/ohos/vap_module/hvigorfile.ts
new file mode 100644
index 00000000..436f2eeb
--- /dev/null
+++ b/ohos/vap_module/hvigorfile.ts
@@ -0,0 +1,6 @@
+import { harTasks } from '@ohos/hvigor-ohos-plugin';
+
+export default {
+ system: harTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
+ plugins:[] /* Custom plugin to extend the functionality of Hvigor. */
+}
diff --git a/ohos/vap_module/obfuscation-rules.txt b/ohos/vap_module/obfuscation-rules.txt
new file mode 100644
index 00000000..c4257353
--- /dev/null
+++ b/ohos/vap_module/obfuscation-rules.txt
@@ -0,0 +1,18 @@
+# Define project specific obfuscation rules here.
+# You can include the obfuscation configuration files in the current module's build-profile.json5.
+#
+# For more details, see
+# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5
+
+# Obfuscation options:
+# -disable-obfuscation: disable all obfuscations
+# -enable-property-obfuscation: obfuscate the property names
+# -enable-toplevel-obfuscation: obfuscate the names in the global scope
+# -compact: remove unnecessary blank spaces and all line feeds
+# -remove-log: remove all console.* statements
+# -print-namecache: print the name cache that contains the mapping from the old names to new names
+# -apply-namecache: reuse the given cache file
+
+# Keep options:
+# -keep-property-name: specifies property names that you want to keep
+# -keep-global-name: specifies names that you want to keep in the global scope
\ No newline at end of file
diff --git a/ohos/vap_module/oh-package-lock.json5 b/ohos/vap_module/oh-package-lock.json5
new file mode 100644
index 00000000..b79e40b3
--- /dev/null
+++ b/ohos/vap_module/oh-package-lock.json5
@@ -0,0 +1,9 @@
+{
+ "meta": {
+ "stableOrder": true
+ },
+ "lockfileVersion": 3,
+ "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.",
+ "specifiers": {},
+ "packages": {}
+}
\ No newline at end of file
diff --git a/ohos/vap_module/oh-package.json5 b/ohos/vap_module/oh-package.json5
new file mode 100644
index 00000000..22d23917
--- /dev/null
+++ b/ohos/vap_module/oh-package.json5
@@ -0,0 +1,9 @@
+{
+ "name": "vap_module",
+ "version": "1.0.0",
+ "description": "A cool video animation playback solution.",
+ "main": "Index.ets",
+ "author": "MaShuai/LiuTianyi",
+ "license": "Apache-2.0",
+ "dependencies": {}
+}
\ No newline at end of file
diff --git a/ohos/vap_module/src/main/cpp/CMakeLists.txt b/ohos/vap_module/src/main/cpp/CMakeLists.txt
new file mode 100644
index 00000000..769f261e
--- /dev/null
+++ b/ohos/vap_module/src/main/cpp/CMakeLists.txt
@@ -0,0 +1,72 @@
+# the minimum version of CMake.
+cmake_minimum_required(VERSION 3.4.1)
+project(XComponent)
+
+set(CMAKE_CXX_STANDARD 17)
+set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
+add_definitions(-DOHOS_PLATFORM)
+
+include_directories(
+ ${NATIVERENDER_ROOT_PATH}
+ ${NATIVERENDER_ROOT_PATH}/include
+ ${NATIVERENDER_ROOT_PATH}/include/util
+ ${NATIVERENDER_ROOT_PATH}/include/render
+ ${NATIVERENDER_ROOT_PATH}/include/mask
+ ${NATIVERENDER_ROOT_PATH}/include/mix
+ ${NATIVERENDER_ROOT_PATH}/include/napi
+ ${NATIVERENDER_ROOT_PATH}/include/util/json/include
+)
+
+#add_subdirectory(${NATIVERENDER_ROOT_PATH}/include/util/json)
+
+add_library(vap SHARED
+ manager/plugin_manager.cpp
+ mask/mask_config.cpp
+ mask/mask_render.cpp
+ mask/mask_shader.cpp
+ mix/frame.cpp
+ mix/mix_render.cpp
+ mix/mix_shader.cpp
+ mix/src.cpp
+ napi/n_val.cpp
+ napi/n_func_arg.cpp
+ render/egl_core.cpp
+ render/plugin_render.cpp
+ render/yuv_render.cpp
+ render/anim_config.cpp
+ util/shader_util.cpp
+ util/tex_coords_util.cpp
+ util/vertex_util.cpp
+ util/texture_load_util.cpp
+ napi_init.cpp
+ video_decoder.cpp
+ demuxer.cpp
+ player.cpp
+ audio_decoder.cpp
+ resource_request.cpp
+ vap_callback.cpp
+)
+
+target_link_libraries(vap PUBLIC EGL)
+target_link_libraries(vap PUBLIC GLESv3)
+target_link_libraries(vap PUBLIC hilog_ndk.z)
+target_link_libraries(vap PUBLIC ace_ndk.z)
+target_link_libraries(vap PUBLIC ace_napi.z)
+target_link_libraries(vap PUBLIC libc++.a)
+target_link_libraries(vap PUBLIC z)
+target_link_libraries(vap PUBLIC uv)
+target_link_libraries(vap PUBLIC libace_napi.z.so)
+target_link_libraries(vap PUBLIC libnative_media_codecbase.so)
+target_link_libraries(vap PUBLIC libnative_media_core.so)
+target_link_libraries(vap PUBLIC libnative_media_vdec.so)
+target_link_libraries(vap PUBLIC libnative_media_adec.so)
+target_link_libraries(vap PUBLIC libnative_media_acodec.so)
+target_link_libraries(vap PUBLIC libnative_media_avsource.so)
+target_link_libraries(vap PUBLIC libnative_media_avdemuxer.so)
+target_link_libraries(vap PUBLIC libavplayer.so)
+target_link_libraries(vap PUBLIC libohaudio.so)
+target_link_libraries(vap PUBLIC libnative_drawing.so)
+target_link_libraries(vap PUBLIC libpixelmap_ndk.z.so)
+target_link_libraries(vap PUBLIC libimage_source.so)
+target_link_libraries(vap PUBLIC libpixelmap.so)
+#target_link_libraries(vap PUBLIC nlohmann_json::nlohmann_json)
\ No newline at end of file
diff --git a/ohos/vap_module/src/main/cpp/audio_decoder.cpp b/ohos/vap_module/src/main/cpp/audio_decoder.cpp
new file mode 100644
index 00000000..cadc1516
--- /dev/null
+++ b/ohos/vap_module/src/main/cpp/audio_decoder.cpp
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "audio_decoder.h"
+
+#include
+#include
+
+#include "log.h"
+
+#undef LOG_TAG
+#define LOG_TAG "AudioDecoder"
+
+namespace {
+void OnCodecError(OH_AVCodec *codec, int32_t errorCode, void *userData)
+{
+ (void)codec;
+ (void)errorCode;
+ (void)userData;
+ LOGE("On codec error, error code: %{public}d", errorCode);
+}
+
+void OnCodecFormatChange(OH_AVCodec *codec, OH_AVFormat *format, void *userData)
+{
+ LOGI("On codec format change");
+}
+
+void OnNeedInputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData)
+{
+ if (userData == nullptr) {
+ return;
+ }
+ (void)codec;
+ ADecSignal *m_signal = static_cast(userData);
+ std::unique_lock lock(m_signal->audioInputMutex);
+ m_signal->audioInputBufferInfoQueue.emplace(index, buffer, codec);
+ m_signal->audioInputCond.notify_all();
+}
+
+void OnNewOutputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData)
+{
+ if (userData == nullptr) {
+ return;
+ }
+ (void)codec;
+ ADecSignal *m_signal = static_cast(userData);
+ std::unique_lock lock(m_signal->audioOutputMutex);
+ m_signal->audioOutputBufferInfoQueue.emplace(index, buffer, codec);
+ m_signal->audioOutputCond.notify_all();
+}
+}
+
+AudioDecoder::~AudioDecoder()
+{
+ Release();
+}
+
+int32_t AudioDecoder::CreateAudioDecoder(const std::string &codecMime)
+{
+ decoder_ = OH_AudioCodec_CreateByMime(codecMime.c_str(), false);
+ if (decoder_ == nullptr) {
+ LOGE("create audio decoder_ failed");
+ return AV_ERR_UNKNOWN;
+ }
+ return AV_ERR_OK;
+}
+
+int32_t AudioDecoder::SetCallback(ADecSignal *signal)
+{
+ int32_t ret = AV_ERR_OK;
+ ret = OH_AudioCodec_RegisterCallback(decoder_, {OnCodecError, OnCodecFormatChange,
+ OnNeedInputBuffer, OnNewOutputBuffer}, signal);
+ if (ret != AV_ERR_OK) {
+ LOGE("Set callback failed, ret: %{public}d", ret);
+ return AV_ERR_UNKNOWN;
+ }
+ return AV_ERR_OK;
+}
+
+int32_t AudioDecoder::ConfigureAudioDecoder(const VAPInfo &sampleInfo)
+{
+ OH_AVFormat *format = OH_AVFormat_Create();
+ if (format == nullptr) {
+ LOGE("AVFormat create failed");
+ return AV_ERR_UNKNOWN;
+ }
+ OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUD_SAMPLE_RATE, sampleInfo.sampleRate);
+ OH_AVFormat_SetLongValue(format, OH_MD_KEY_BITRATE, sampleInfo.audioBitrate);
+ OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUD_CHANNEL_COUNT, sampleInfo.channelCount);
+ if (strcmp(sampleInfo.audioCodecMime.c_str(), "audio/mp4a-latm") == 0) {
+ OH_AVFormat_SetIntValue(format, OH_MD_KEY_AAC_IS_ADTS, 1);
+ LOGD("audio mime is aac");
+ } else {
+ LOGD("audio mime is not aac");
+ }
+ LOGD("AudioDecoder config: %{public}d - %{public}ld = %{public}d", sampleInfo.sampleRate,
+ sampleInfo.audioBitrate, sampleInfo.channelCount);
+ int ret = OH_AudioCodec_Configure(decoder_, format);
+ if (ret != AV_ERR_OK) {
+ LOGE("Config failed, ret: %{public}d", ret);
+ return AV_ERR_UNKNOWN;
+ }
+ OH_AVFormat_Destroy(format);
+ format = nullptr;
+
+ return AV_ERR_OK;
+}
+
+int32_t AudioDecoder::Config(const VAPInfo &sampleInfo, ADecSignal *signal)
+{
+ if (decoder_ == nullptr) {
+ LOGE("Decoder is null");
+ return AV_ERR_UNKNOWN;
+ }
+ if (signal == nullptr) {
+ LOGE("Invalid param: codecUserData");
+ return AV_ERR_UNKNOWN;
+ }
+
+ int32_t ret = ConfigureAudioDecoder(sampleInfo);
+ if (ret != AV_ERR_OK) {
+ LOGE("Configure failed");
+ return AV_ERR_UNKNOWN;
+ }
+
+ ret = SetCallback(signal);
+ if (ret != AV_ERR_OK) {
+ LOGE("Set callback failed, ret: %{public}d", ret);
+ return AV_ERR_UNKNOWN;
+ }
+
+ {
+ int ret = OH_AudioCodec_Prepare(decoder_);
+ if (ret != AV_ERR_OK) {
+ LOGE("audio Prepare failed, ret: %{public}d", ret);
+ return AV_ERR_UNKNOWN;
+ }
+ }
+
+ return AV_ERR_OK;
+}
+
+int32_t AudioDecoder::StartAudioDecoder()
+{
+ if (decoder_ == nullptr) {
+ LOGE("Decoder is null");
+ return AV_ERR_UNKNOWN;
+ }
+
+ int ret = OH_AudioCodec_Start(decoder_);
+ if (ret != AV_ERR_OK) {
+ LOGE("audio Start failed, ret: %{public}d", ret);
+ return AV_ERR_UNKNOWN;
+ }
+ return AV_ERR_OK;
+}
+
+int32_t AudioDecoder::PushInputData(AudioCodecBufferInfo &info)
+{
+ if (decoder_ == nullptr) {
+ LOGE("Decoder is null");
+ return AV_ERR_UNKNOWN;
+ }
+ int32_t ret = OH_AVBuffer_SetBufferAttr(info.bufferOrigin, &info.attr);
+ if (ret != AV_ERR_OK) {
+ LOGE("Set avbuffer attr failed");
+ return AV_ERR_UNKNOWN;
+ }
+ ret = OH_AudioCodec_PushInputBuffer(decoder_, info.bufferIndex);
+ if (ret != AV_ERR_OK) {
+ LOGE("Push input data failed");
+ return AV_ERR_UNKNOWN;
+ }
+ return AV_ERR_OK;
+}
+
+int32_t AudioDecoder::FreeOutputData(uint32_t bufferIndex, bool render)
+{
+ if (decoder_ == nullptr) {
+ LOGE("Decoder is null");
+ return AV_ERR_UNKNOWN;
+ }
+
+ int32_t ret = AV_ERR_OK;
+ ret = OH_AudioCodec_FreeOutputBuffer(decoder_, bufferIndex);
+ if (ret != AV_ERR_OK) {
+ LOGE("audio Free output data failed");
+ return AV_ERR_UNKNOWN;
+ }
+ return AV_ERR_OK;
+}
+
+int32_t AudioDecoder::Release()
+{
+ if (decoder_ != nullptr) {
+ OH_AudioCodec_Flush(decoder_);
+ OH_AudioCodec_Stop(decoder_);
+ OH_AudioCodec_Destroy(decoder_);
+ decoder_ = nullptr;
+ }
+ return AV_ERR_OK;
+}
diff --git a/ohos/vap_module/src/main/cpp/demuxer.cpp b/ohos/vap_module/src/main/cpp/demuxer.cpp
new file mode 100644
index 00000000..8764fb27
--- /dev/null
+++ b/ohos/vap_module/src/main/cpp/demuxer.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include
+#include "demuxer.h"
+
+Demuxer::~Demuxer()
+{
+ Release();
+}
+
+int32_t Demuxer::CreateDemuxer(VAPInfo &info)
+{
+ source_ = OH_AVSource_CreateWithFD(info.inputFd, info.inputFileOffset, info.inputFileSize);
+ if (source_ == nullptr) {
+ LOGE("create source_ failed");
+ return AV_ERR_UNKNOWN;
+ }
+
+ demuxer_ = OH_AVDemuxer_CreateWithSource(source_);
+ if (demuxer_ == nullptr) {
+ LOGE("create demuxer_ failed");
+ return AV_ERR_UNKNOWN;
+ }
+
+ auto sourceFormat = std::shared_ptr(OH_AVSource_GetSourceFormat(source_), OH_AVFormat_Destroy);
+ if (sourceFormat == nullptr) {
+ LOGE("get source_ format failed");
+ return AV_ERR_UNKNOWN;
+ }
+
+ int32_t ret = GetTrackInfo(sourceFormat, info);
+ if (ret != AV_ERR_OK) {
+ LOGE("get track info failed");
+ return AV_ERR_UNKNOWN;
+ }
+ return AV_ERR_OK;
+}
+
+int32_t Demuxer::GetTrackInfo(std::shared_ptr sourceFormat, VAPInfo &info)
+{
+ int32_t trackCount = 0;
+ OH_AVFormat_GetIntValue(sourceFormat.get(), OH_MD_KEY_TRACK_COUNT, &trackCount);
+ for (int32_t index = 0; index < trackCount; index++) {
+ int trackType = -1;
+ auto trackFormat = std::shared_ptr(OH_AVSource_GetTrackFormat(source_, index),
+ OH_AVFormat_Destroy);
+ OH_AVFormat_GetIntValue(trackFormat.get(), OH_MD_KEY_TRACK_TYPE, &trackType);
+ if (trackType == MEDIA_TYPE_VID) {
+ OH_AVDemuxer_SelectTrackByID(demuxer_, index);
+ OH_AVFormat_GetIntValue(trackFormat.get(), OH_MD_KEY_WIDTH, &info.videoWidth);
+ OH_AVFormat_GetIntValue(trackFormat.get(), OH_MD_KEY_HEIGHT, &info.videoHeight);
+ OH_AVFormat_GetDoubleValue(trackFormat.get(), OH_MD_KEY_FRAME_RATE, &info.frameRate);
+ OH_AVFormat_GetLongValue(trackFormat.get(), OH_MD_KEY_BITRATE, &info.bitrate);
+ OH_AVFormat_GetIntValue(trackFormat.get(), "video_is_hdr_vivid", &info.isHDRVivid);
+ OH_AVFormat_GetIntValue(trackFormat.get(), OH_MD_KEY_ROTATION, &info.rotation);
+ char *codecMime;
+ OH_AVFormat_GetStringValue(trackFormat.get(), OH_MD_KEY_CODEC_MIME, const_cast(&codecMime));
+ info.codecMime = codecMime;
+ OH_AVFormat_GetIntValue(trackFormat.get(), OH_MD_KEY_PROFILE, &info.hevcProfile);
+ videoTrackId_ = index;
+
+ LOGI("Demuxer config: %{public}d*%{public}d, %{public}.1ffps, %{public}ld" "kbps",
+ info.videoWidth, info.videoHeight, info.frameRate, info.bitrate / ONE_K);
+ } else if (trackType == MEDIA_TYPE_AUD) {
+ OH_AVDemuxer_SelectTrackByID(demuxer_, index);
+ OH_AVFormat_GetLongValue(trackFormat.get(), OH_MD_KEY_BITRATE, &info.audioBitrate);
+ OH_AVFormat_GetIntValue(trackFormat.get(),
+ OH_MD_KEY_AUD_SAMPLE_RATE, reinterpret_cast(&info.sampleRate));
+ OH_AVFormat_GetIntValue(trackFormat.get(),
+ OH_MD_KEY_AUD_CHANNEL_COUNT, reinterpret_cast(&info.channelCount));
+
+ char *audioCodecMime;
+ OH_AVFormat_GetStringValue(trackFormat.get(),
+ OH_MD_KEY_CODEC_MIME, const_cast(&audioCodecMime));
+ info.audioCodecMime = audioCodecMime;
+ audioTrackId_ = index;
+
+ LOGI("Audio Demuxer config: %{public}d %{public}d, %{public}ld"
+ "kbps",
+ info.channelCount, info.sampleRate, info.audioBitrate / ONE_K);
+ }
+ }
+ return AV_ERR_OK;
+}
+
+int32_t Demuxer::ReadSample(OH_AVBuffer *buffer, OH_AVCodecBufferAttr &attr)
+{
+ int32_t ret = OH_AVDemuxer_ReadSampleBuffer(demuxer_, videoTrackId_, buffer);
+ if (ret != AV_ERR_OK) {
+ LOGE("read sample failed");
+ return AV_ERR_UNKNOWN;
+ }
+
+ ret = OH_AVBuffer_GetBufferAttr(buffer, &attr);
+ if (ret != AV_ERR_OK) {
+ LOGE("get buffer attr failed");
+ return AV_ERR_UNKNOWN;
+ }
+ return AV_ERR_OK;
+}
+
+int32_t Demuxer::ReadAudioSample(OH_AVBuffer *buffer, OH_AVCodecBufferAttr &attr)
+{
+ int32_t ret = OH_AVDemuxer_ReadSampleBuffer(demuxer_, audioTrackId_, buffer);
+ if (ret != AV_ERR_OK) {
+ LOGE("read audio sample failed");
+ return AV_ERR_UNKNOWN;
+ }
+
+ ret = OH_AVBuffer_GetBufferAttr(buffer, &attr);
+ if (ret != AV_ERR_OK) {
+ LOGE("get audio buffer attr failed");
+ return AV_ERR_UNKNOWN;
+ }
+ return AV_ERR_OK;
+}
+
+int32_t Demuxer::Release()
+{
+ if (source_ != nullptr) {
+ OH_AVSource_Destroy(source_);
+ source_ = nullptr;
+ }
+ if (demuxer_ != nullptr) {
+ OH_AVDemuxer_Destroy(demuxer_);
+ demuxer_ = nullptr;
+ }
+ return AV_ERR_OK;
+}
diff --git a/ohos/vap_module/src/main/cpp/include/audio_decoder.h b/ohos/vap_module/src/main/cpp/include/audio_decoder.h
new file mode 100644
index 00000000..98fbd99d
--- /dev/null
+++ b/ohos/vap_module/src/main/cpp/include/audio_decoder.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VAP_AUDIO_DECODER_H
+#define VAP_AUDIO_DECODER_H
+
+#include "multimedia/player_framework/native_avcodec_videodecoder.h"
+#include "multimedia/player_framework/native_avbuffer_info.h"
+#include "data_info.h"
+
+class AudioDecoder {
+public:
+ AudioDecoder() = default;
+ ~AudioDecoder();
+
+ int32_t CreateAudioDecoder(const std::string &codecMime);
+ int32_t ConfigureAudioDecoder(const VAPInfo &sampleInfo);
+ int32_t Config(const VAPInfo &sampleInfo, ADecSignal *signal);
+ int32_t StartAudioDecoder();
+ int32_t PushInputData(AudioCodecBufferInfo &info);
+ int32_t FreeOutputData(uint32_t bufferIndex, bool render);
+ int32_t Release();
+
+private:
+ int32_t SetCallback(ADecSignal *signal);
+
+ bool isAVBufferMode_ = false;
+ OH_AVCodec *decoder_ = nullptr;
+};
+#endif // VAP_AUDIO_DECODER_H
\ No newline at end of file
diff --git a/ohos/vap_module/src/main/cpp/include/common_const.h b/ohos/vap_module/src/main/cpp/include/common_const.h
new file mode 100644
index 00000000..fc112523
--- /dev/null
+++ b/ohos/vap_module/src/main/cpp/include/common_const.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COMMON_CONST_H
+#define COMMON_CONST_H
+
+#include
+
+namespace CommonConst {
+ static constexpr int32_t ZERO = 0;
+ static constexpr int32_t ONE = 1;
+ static constexpr int32_t TWO = 2;
+ static constexpr int32_t THREE = 3;
+ static constexpr int32_t FOUR = 4;
+ static constexpr int32_t FIVE = 5;
+ static constexpr int32_t SIX = 6;
+ static constexpr int32_t SEVEN = 7;
+ static constexpr int32_t EIGHT = 8;
+
+ static constexpr int32_t THREE_TIME_EIGHT = 24;
+ static constexpr int32_t TWO_TIME_EIGHT = 16;
+
+ static constexpr int32_t NEGATIVE_ONE = -1;
+ static constexpr int32_t NINETY_DEGREES = 90;
+
+ static constexpr int32_t ONE_EIGHT_HEX = 0xff;
+}
+
+#endif
diff --git a/ohos/vap_module/src/main/cpp/include/data_info.h b/ohos/vap_module/src/main/cpp/include/data_info.h
new file mode 100644
index 00000000..98ad18cb
--- /dev/null
+++ b/ohos/vap_module/src/main/cpp/include/data_info.h
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VAP_DATATYPE_INFO_H
+#define VAP_DATATYPE_INFO_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "mix_render.h"
+#include "multimedia/player_framework/native_avcodec_base.h"
+#include "multimedia/player_framework/native_avbuffer.h"
+
+#define ANNEXB_INPUT_ONLY 1
+
+const std::string_view MIME_VIDEO_AVC = "video/avc";
+const std::string_view MIME_VIDEO_HEVC = "video/hevc";
+
+constexpr int32_t BITRATE_10M = 10 * 1024 * 1024; // 10Mbps
+constexpr int32_t BITRATE_20M = 20 * 1024 * 1024; // 20Mbps
+constexpr int32_t BITRATE_30M = 30 * 1024 * 1024; // 30Mbps
+
+struct AudioInitData {
+ OH_AudioStream_Type type = AUDIOSTREAM_TYPE_RENDERER;
+ int32_t samplingRate = 48000;
+ int32_t channelCount = 2;
+ OH_AudioStream_SampleFormat format = AUDIOSTREAM_SAMPLE_S16LE;
+ OH_AudioStream_EncodingType encodingType = AUDIOSTREAM_ENCODING_TYPE_RAW;
+ OH_AudioStream_Usage usage = AUDIOSTREAM_USAGE_MUSIC;
+};
+
+struct VAPInfo {
+ int32_t sampleId = 0;
+ int32_t inputFd = -1;
+ int32_t outFd = -1;
+ int64_t inputFileOffset = 0;
+ int64_t inputFileSize = 0;
+ std::string inputFilePath;
+ std::string outputFilePath;
+ std::string codecMime = MIME_VIDEO_AVC.data();
+ int32_t videoWidth = 0;
+ int32_t videoHeight = 0;
+ double frameRate = 0.0;
+ int64_t bitrate = 10 * 1024 * 1024; // 10Mbps;
+
+ int64_t audioBitrate;
+ uint32_t sampleRate;
+ uint32_t channelCount;
+ std::string audioCodecMime;
+
+ int64_t frameInterval = 0;
+ int32_t perfmode = 0;
+ int64_t durationTime = 0;
+ uint32_t maxFrames = UINT32_MAX;
+ int32_t isHDRVivid = 0;
+ uint32_t repeatTimes = 1;
+ OH_AVPixelFormat pixelFormat = AV_PIXEL_FORMAT_NV12; // AV_PIXEL_FORMAT_YUVI420;
+ bool needDumpOutput = false;
+ uint32_t bitrateMode = CBR;
+ int32_t hevcProfile = HEVC_PROFILE_MAIN;
+ int32_t rotation = 0;
+ NativeWindow* window = nullptr;
+
+ uint32_t bufferSize = 0;
+ double readTime = 0;
+ double memcpyTime = 0;
+ double writeTime = 0;
+
+ void (*playDoneCallback)(void *context) = nullptr;
+ void *playDoneCallbackData = nullptr;
+
+ int32_t width;
+ int32_t height;
+ std::string uri;
+ std::map iptData;
+};
+
+struct CodecBufferInfo {
+ uint32_t bufferIndex = 0;
+ uintptr_t *buffer = nullptr;
+ uint8_t *bufferAddr = nullptr;
+ OH_AVCodec *codec = nullptr;
+ OH_AVBuffer *bufferOrigin = nullptr;
+ OH_AVCodecBufferAttr attr = {0, 0, 0, AVCODEC_BUFFER_FLAGS_NONE};
+
+ CodecBufferInfo(uint8_t *addr) : bufferAddr(addr){};
+ CodecBufferInfo(uint8_t *addr, int32_t bufferSize)
+ : bufferAddr(addr), attr({0, bufferSize, 0, AVCODEC_BUFFER_FLAGS_NONE}){};
+ CodecBufferInfo(uint32_t argBufferIndex, OH_AVMemory *argBuffer, OH_AVCodecBufferAttr argAttr)
+ : bufferIndex(argBufferIndex), buffer(reinterpret_cast(argBuffer)), attr(argAttr){};
+ CodecBufferInfo(uint32_t argBufferIndex, OH_AVMemory *argBuffer)
+ : bufferIndex(argBufferIndex), buffer(reinterpret_cast(argBuffer)){};
+ CodecBufferInfo(uint32_t argBufferIndex, OH_AVBuffer *argBuffer)
+ : bufferIndex(argBufferIndex), buffer(reinterpret_cast(argBuffer))
+ {
+ OH_AVBuffer_GetBufferAttr(argBuffer, &attr);
+ };
+
+ CodecBufferInfo(uint32_t argBufferIndex, OH_AVBuffer *argBuffer, OH_AVCodec *argCodec)
+ : bufferIndex(argBufferIndex), bufferOrigin(argBuffer), codec(argCodec)
+ {
+ OH_AVBuffer_GetBufferAttr(argBuffer, &attr);
+ };
+};
+
+struct AudioCodecBufferInfo {
+ uint32_t bufferIndex = 0;
+ uint8_t *bufferAddr = nullptr;
+ OH_AVCodec *codec = nullptr;
+ OH_AVBuffer *bufferOrigin = nullptr;
+ OH_AVCodecBufferAttr attr = {0, 0, 0, AVCODEC_BUFFER_FLAGS_NONE};
+
+ AudioCodecBufferInfo(uint32_t argBufferIndex, OH_AVBuffer *argBuffer, OH_AVCodec *argCodec)
+ : bufferIndex(argBufferIndex), bufferOrigin(argBuffer), codec(argCodec)
+ {
+ OH_AVBuffer_GetBufferAttr(argBuffer, &attr);
+ };
+};
+
+class ADecSignal {
+public:
+ std::mutex audioInputMutex;
+ std::condition_variable audioInputCond;
+ std::queue audioInputBufferInfoQueue;
+ std::mutex audioOutputMutex;
+ std::condition_variable audioOutputCond;
+ std::queue audioOutputBufferInfoQueue;
+ std::mutex renderMutex;
+ std::condition_variable renderCond;
+ std::queue renderQueue;
+ bool isInterrupt = false;
+
+ void ClearQueue()
+ {
+ {
+ std::unique_lock lock(audioInputMutex);
+ auto emptyQueue = std::queue();
+ audioInputBufferInfoQueue.swap(emptyQueue);
+ }
+ {
+ std::unique_lock lock(audioOutputMutex);
+ auto emptyQueue = std::queue();
+ audioOutputBufferInfoQueue.swap(emptyQueue);
+ }
+ }
+};
+
+class VDecSignal {
+public:
+ uint32_t inputFrameCount = 0;
+ std::mutex inputMutex;
+ std::condition_variable inputCond;
+ std::queue inputBufferInfoQueue;
+
+ uint32_t outputFrameCount = 0;
+ std::mutex outputMutex;
+ std::condition_variable outputCond;
+ std::queue outputBufferInfoQueue;
+
+ void ClearQueue()
+ {
+ {
+ std::unique_lock lock(inputMutex);
+ auto emptyQueue = std::queue();
+ inputBufferInfoQueue.swap(emptyQueue);
+ }
+ {
+ std::unique_lock lock(outputMutex);
+ auto emptyQueue = std::queue();
+ outputBufferInfoQueue.swap(emptyQueue);
+ }
+ }
+};
+
+enum VapState {
+ UNKNOWN,
+ READY,
+ START,
+ RENDER,
+ COMPLETE,
+ DESTROY,
+ FAILED
+};
+
+struct JSAnimConfig {
+ int32_t version;
+ int32_t totalFrames;
+ int32_t width;
+ int32_t height;
+ int32_t videoWidth;
+ int32_t videoHeight;
+ Orien orien;
+ int32_t fps;
+ bool isMix;
+ PointRect alphaPointRect;
+ PointRect rgbPointRect;
+ int32_t currentFrame;
+};
+
+enum class VideoFitType {
+ FIT_XY,
+ FIT_CENTER,
+ CENTER_CROP
+};
+
+typedef struct CallbackContext {
+ napi_env env = nullptr;
+ napi_ref callbackRef = nullptr;
+ VapState vapState = VapState::UNKNOWN;
+ int32_t err;
+ JSAnimConfig jsAnimConfig;
+} CallbackContext;
+
+#endif // VAP_DATATYPE_INFO_H
\ No newline at end of file
diff --git a/ohos/vap_module/src/main/cpp/include/demuxer.h b/ohos/vap_module/src/main/cpp/include/demuxer.h
new file mode 100644
index 00000000..18d1740c
--- /dev/null
+++ b/ohos/vap_module/src/main/cpp/include/demuxer.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VAP_DEMUXER_H
+#define VAP_DEMUXER_H
+
+#include
+#include "multimedia/player_framework/native_avdemuxer.h"
+#include "data_info.h"
+
+static constexpr int32_t ONE_K = 1024;
+
+class Demuxer {
+public:
+ ~Demuxer();
+ int32_t CreateDemuxer(VAPInfo &sampleInfo);
+ int32_t GetTrackInfo(std::shared_ptr sourceFormat, VAPInfo &info);
+ int32_t ReadSample(OH_AVBuffer *buffer, OH_AVCodecBufferAttr &attr);
+ int32_t ReadAudioSample(OH_AVBuffer *buffer, OH_AVCodecBufferAttr &attr);
+ int32_t Release();
+
+ bool hasAudio() { return audioTrackId_ != -1; }
+ bool hasVideo() { return videoTrackId_ != -1; }
+
+private:
+ OH_AVSource *source_ = nullptr;
+ OH_AVDemuxer *demuxer_ = nullptr;
+ int32_t videoTrackId_ = -1;
+ int32_t audioTrackId_ = -1;
+};
+
+#endif // VAP_DEMUXER_H
\ No newline at end of file
diff --git a/ohos/vap_module/src/main/cpp/include/mask/mask_config.h b/ohos/vap_module/src/main/cpp/include/mask/mask_config.h
new file mode 100644
index 00000000..c743a72a
--- /dev/null
+++ b/ohos/vap_module/src/main/cpp/include/mask/mask_config.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VAP_MASK_CONFIG_H
+#define VAP_MASK_CONFIG_H
+
+#include
+#include
+
+#include "texture_load_util.h"
+#include "vertex_util.h"
+
+class MaskConfig {
+public:
+ MaskConfig(int32_t width, int32_t height);
+ ~MaskConfig();
+
+ int32_t GetMaskTexId();
+ int32_t UpdateMaskTex(BitMap alphaMaskBitmap);
+
+public:
+ std::tuple maskTexPair_ =
+ std::move(std::make_tuple(PointRect(0, 0, 0, 0), RefVec2(0, 0)));
+
+ std::tuple maskPositionPair_ =
+ std::move(std::make_tuple(PointRect(0, 0, 0, 0), RefVec2(0, 0)));
+
+ int32_t width_; // animConfig width
+ int32_t height_;
+private:
+ GLuint maskTexId_ = 0;
+};
+
+#endif // VAP_MASK_CONFIG_H
diff --git a/ohos/vap_module/src/main/cpp/include/mask/mask_render.h b/ohos/vap_module/src/main/cpp/include/mask/mask_render.h
new file mode 100644
index 00000000..053cf5ef
--- /dev/null
+++ b/ohos/vap_module/src/main/cpp/include/mask/mask_render.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VAP_MASK_RENDER_H
+#define VAP_MASK_RENDER_H
+
+#include "mask_config.h"
+#include "mask_shader.h"
+#include "vertex_util.h"
+
+class MaskRender {
+public:
+ MaskRender(bool edgeBlur);
+ void RenderFrame(MaskConfig maskConfig);
+
+public:
+ std::unique_ptr m_maskShader;
+ VertexUtil m_vertexArray;
+
+private:
+ VertexUtil m_maskArray;
+};
+
+#endif // VAP_MASK_RENDER_H
diff --git a/ohos/vap_module/src/main/cpp/include/mask/mask_shader.h b/ohos/vap_module/src/main/cpp/include/mask/mask_shader.h
new file mode 100644
index 00000000..ed981c32
--- /dev/null
+++ b/ohos/vap_module/src/main/cpp/include/mask/mask_shader.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VAP_MASK_SHADER_H
+#define VAP_MASK_SHADER_H
+
+#include
+#include "shader_util.h"
+
+static constexpr char MASK_VERTEX[] =
+ "attribute vec4 vPosition;\n"
+ "attribute vec4 vTexCoordinateAlphaMask;\n"
+ "varying vec2 v_TexCoordinateAlphaMask;\n"
+ "\n"
+ "void main() {\n"
+ " v_TexCoordinateAlphaMask = vec2(vTexCoordinateAlphaMask.x, vTexCoordinateAlphaMask.y);\n"
+ " gl_Position = vPosition;\n"
+ "}\n\0";
+
+static constexpr char FRAGMENT_BLUR_EDGE[] =
+ "precision mediump float;\n"
+ "uniform sampler2D uTextureAlphaMask;\n"
+ "varying vec2 v_TexCoordinateAlphaMask;\n"
+ "mat3 weight = mat3(0.0625,0.125,0.0625,0.125,0.25,0.125,0.0625,0.125,0.0625);\n "
+ "int coreSize=3;\n"
+ "float texelOffset = .01;\n"
+ "\n"
+ "void main() {\n"
+ " float alphaResult = 0.;\n"
+ " for(int y = 0; y < coreSize; y++) {\n"
+ " for(int x = 0;x < coreSize; x++) {\n"
+ " alphaResult += texture2D(uTextureAlphaMask, vec2(v_TexCoordinateAlphaMask.x + (-1.0 + float(x)) * "
+ "texelOffset,v_TexCoordinateAlphaMask.y + (-1.0 + float(y)) * texelOffset)).a * weight[x][y];\n"
+ " }\n"
+ " }\n"
+ " gl_FragColor = vec4(0, 0, 0, alphaResult);\n"
+ "}\n\0";
+
+static constexpr char FRAGMENT_NO_BLUR_EDGE[] =
+ "precision mediump float;\n"
+ "uniform sampler2D uTextureAlphaMask;\n"
+ "varying vec2 v_TexCoordinateAlphaMask;\n"
+ "\n"
+ "void main () {\n"
+ " vec4 alphaMaskColor = texture2D(uTextureAlphaMask, v_TexCoordinateAlphaMask);\n"
+ " gl_FragColor = vec4(0, 0, 0, alphaMaskColor.a);\n"
+ "}\n\0";
+
+static constexpr char FRAGMENT_ROW[] =
+ "precision mediump float;\n"
+ "uniform sampler2D uTextureAlphaMask;\n"
+ "varying vec2 v_TexCoordinateAlphaMask;\n"
+ "vec3 weight = vec3(0.4026,0.2442,0.0545);\n "
+ "\n"
+ "void main() {\n"
+ " float texelOffset = .01;\n"
+ " vec2 uv[5];\n"
+ " uv[0]= v_TexCoordinateAlphaMask;\n"
+ " uv[1]=vec2(uv[0].x+texelOffset*1.0, uv[0].y);\n"
+ " uv[2]=vec2(uv[0].x-texelOffset*1.0, uv[0].y);\n"
+ " uv[3]=vec2(uv[0].x+texelOffset*2.0, uv[0].y);\n"
+ " uv[4]=vec2(uv[0].x-texelOffset*2.0, uv[0].y);\n"
+ " float alphaResult = texture2D(uTextureAlphaMask, uv[0]).a * weight[0];\n"
+ " for(int i = 1; i < 3; ++i) {\n"
+ " alphaResult += texture2D(uTextureAlphaMask, uv[2*i-1]).a * weight[i];\n"
+ " alphaResult += texture2D(uTextureAlphaMask, uv[2*i]).a * weight[i];\n"
+ " }\n"
+ " gl_FragColor = vec4(0, 0, 0, alphaResult);\n"
+ "}\n\0";
+
+class MaskShader {
+public:
+ MaskShader(bool edgeBlurBoolean = false);
+
+ void UseProgram();
+
+public:
+ int32_t m_uTextureMaskUnitLocation;
+ int32_t m_aPositionLocation;
+ int32_t m_aTextureMaskCoordinatesLocation;
+
+private:
+ int32_t m_program;
+
+private:
+ const char *U_TEXTURE_ALPHA_MASK_UNIT = "uTextureAlphaMask";
+ const char *A_POSITION = "vPosition";
+ const char *A_TEXTURE_MASK_COORDINATES = "vTexCoordinateAlphaMask";
+};
+
+#endif // VAP_MASK_SHADER_H
\ No newline at end of file
diff --git a/ohos/vap_module/src/main/cpp/include/mix/frame.h b/ohos/vap_module/src/main/cpp/include/mix/frame.h
new file mode 100644
index 00000000..06b39fa6
--- /dev/null
+++ b/ohos/vap_module/src/main/cpp/include/mix/frame.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VAP_FRAME_H
+#define VAP_FRAME_H
+
+#include
+#include
+
+#include
+#include "vertex_util.h"
+
+using json = nlohmann::json;
+
+class Frame {
+public:
+ Frame(json jsonObj);
+
+ std::string srcId = "";
+ int32_t z = 0;
+ PointRect frame;
+ PointRect mFrame;
+ int32_t mt = 0; // 遮罩旋转角度v2 版本只支持 0 与 90度
+
+ bool operator<(const Frame &f) const { return z < f.z; }
+ void PrintInfo();
+};
+
+class FrameSet {
+public:
+ FrameSet(json jsonObj);
+
+ std::vector frames;
+ int index;
+};
+
+class FrameAll {
+public:
+ FrameAll(json jsonObj);
+
+ std::map frameAll;
+};
+#endif // VAP_FRAME_H
diff --git a/ohos/vap_module/src/main/cpp/include/mix/mix_render.h b/ohos/vap_module/src/main/cpp/include/mix/mix_render.h
new file mode 100644
index 00000000..361935a7
--- /dev/null
+++ b/ohos/vap_module/src/main/cpp/include/mix/mix_render.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VAP_MIX_RENDER_H
+#define VAP_MIX_RENDER_H
+
+#include
+#include
+#include
+
+#include "anim_config.h"
+#include "frame.h"
+#include "mix_shader.h"
+#include "resource_request.h"
+#include "src.h"
+#include "texture_load_util.h"
+#include "vertex_util.h"
+
+struct MixConfigSize {
+ int32_t width;
+ int32_t height;
+ int32_t videoWidth;
+ int32_t videoHeight;
+};
+
+#define SET_COLOR 0x01
+#define SET_TEXT_ALIGN 0x02
+#define SET_FONT_WEIGHT 0x04
+
+struct MixInputData {
+ std::string txt = "DEFAULT_TEXT";
+ std::string imgUri = "DEFAULT_URI";
+
+ uint8_t isSet = 0;
+ ColorARGB color;
+ OH_Drawing_TextAlign textAlign;
+ OH_Drawing_FontWeight fontWeight;
+};
+
+class MixRender {
+public:
+ MixRender(std::map iptData);
+ ~MixRender();
+
+ void Init();
+ void GenSrcTexture(Src src, BitMap &bitmap, const MixInputData mixData);
+ void RenderFrame(MixConfigSize config, Frame frame, Src src);
+ void SetVideoTextureId(GLuint id);
+ void SetAnimConfig(std::shared_ptr animConfig)
+ {
+ m_animConfig = animConfig;
+ Init();
+ }
+
+ GLuint videoTextureId;
+ std::unique_ptr m_shader;
+ VertexUtil m_vertexArray;
+ VertexUtil m_srcArray;
+ VertexUtil m_maskArray;
+ std::shared_ptr m_animConfig;
+ bool haveSrc = false;
+
+private:
+ void GenSrcCoordsArray(VertexUtil &array, int32_t fw, int32_t fh, int32_t sw, int32_t sh, FitType fitType);
+
+ std::vector m_textureIds;
+ std::map m_mixData;
+};
+
+#endif // VAP_MIX_RENDER_H
diff --git a/ohos/vap_module/src/main/cpp/include/mix/mix_shader.h b/ohos/vap_module/src/main/cpp/include/mix/mix_shader.h
new file mode 100644
index 00000000..ecf6df06
--- /dev/null
+++ b/ohos/vap_module/src/main/cpp/include/mix/mix_shader.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VAP_MIX_SHADER_H
+#define VAP_MIX_SHADER_H
+
+#include
+
+#include "shader_util.h"
+
+static constexpr char VERTEX[] =
+ "attribute vec4 a_Position;\n"
+ "attribute vec2 a_TextureSrcCoordinates;\n"
+ "attribute vec2 a_TextureMaskCoordinates;\n"
+ "varying vec2 v_TextureSrcCoordinates;\n"
+ "varying vec2 v_TextureMaskCoordinates;\n"
+ "void main()\n"
+ "{\n"
+ " v_TextureSrcCoordinates = a_TextureSrcCoordinates;\n"
+ " v_TextureMaskCoordinates = a_TextureMaskCoordinates;\n"
+ " gl_Position = a_Position;\n"
+ "}\n\0";
+
+static constexpr char FRAGMENT[] =
+ "#extension GL_OES_EGL_image_external : require\n"
+ "precision mediump float; \n"
+ "uniform sampler2D u_TextureSrcUnit;\n"
+ "uniform sampler2D u_TextureMaskUnit;\n"
+ "uniform int u_isFill;\n"
+ "uniform vec4 u_Color;\n"
+ "varying vec2 v_TextureSrcCoordinates;\n"
+ "varying vec2 v_TextureMaskCoordinates;\n"
+ "void main()\n"
+ "{\n"
+ " vec4 srcRgba = texture2D(u_TextureSrcUnit, v_TextureSrcCoordinates);\n"
+ " vec4 maskRgba = texture2D(u_TextureMaskUnit, v_TextureMaskCoordinates);\n"
+ " float isFill = step(0.5, float(u_isFill));\n"
+ " vec4 srcRgbaCal = isFill * vec4(u_Color.r, u_Color.g, u_Color.b, srcRgba.a) + (1.0 - isFill) * srcRgba;\n"
+ " gl_FragColor = vec4(srcRgbaCal.r, srcRgbaCal.g, srcRgbaCal.b, srcRgba.a * maskRgba.r);\n"
+ "}\n\0";
+
+class MixShader {
+public:
+ MixShader();
+ void UseProgram();
+
+public:
+ // Shader program
+ int32_t m_program;
+
+ // Uniform locations
+ int32_t m_uTextureSrcUnitLocation;
+ int32_t m_uTextureMaskUnitLocation;
+ int32_t m_uIsFillLocation;
+ int32_t m_uColorLocation;
+
+ // Attribute locations
+ int32_t m_aPositionLocation;
+ int32_t m_aTextureSrcCoordinatesLocation;
+ int32_t m_aTextureMaskCoordinatesLocation;
+
+private:
+ const char *U_TEXTURE_SRC_UNIT = "u_TextureSrcUnit";
+ const char *U_TEXTURE_MASK_UNIT = "u_TextureMaskUnit";
+ const char *U_IS_FILL = "u_isFill";
+ const char *U_COLOR = "u_Color";
+
+ const char *A_POSITION = "a_Position";
+ const char *A_TEXTURE_SRC_COORDINATES = "a_TextureSrcCoordinates";
+ const char *A_TEXTURE_MASK_COORDINATES = "a_TextureMaskCoordinates";
+};
+
+#endif // VAP_MIX_SHADER_H
diff --git a/ohos/vap_module/src/main/cpp/include/mix/src.h b/ohos/vap_module/src/main/cpp/include/mix/src.h
new file mode 100644
index 00000000..beff022a
--- /dev/null
+++ b/ohos/vap_module/src/main/cpp/include/mix/src.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VAP_SRC_H
+#define VAP_SRC_H
+
+#include