feat: package core sdk and integrate velix player app
This commit is contained in:
1 parent
e594f66c35
commit
14149854b8
14 files changed
+928
-4
No files matched your search
@@ -2,5 +2,6 @@
|
||||
!/subprojects/packagefiles
|
||||
!/subprojects/*.wrap
|
||||
/build/
|
||||
/dist/
|
||||
.hvigor/
|
||||
.DS_Store
|
||||
@@ -0,0 +1,11 @@
|
||||
# Core API versioning
|
||||
|
||||
当前冻结版本:`VELIX_CORE_API_VERSION = 0x000100`(v0)。
|
||||
|
||||
- `MAJOR` 变化表示 ABI/API 不兼容,必须升级主 App 适配层。
|
||||
- `MINOR` 变化只增加尾部字段或能力,旧调用方保持可用。
|
||||
- `PATCH` 变化只修复实现,不改变公开结构布局。
|
||||
|
||||
版本宏、`velix_core_get_api_version()`、`velix_core_get_version()` 和 `velix_core_get_build_info()` 位于 `include/velix/core.h`,C ABI 使用 opaque handle 与固定宽度整数。变更 API 时同步更新 `metadata/build-info.json`、此文档与集成文档,并在发布前检查动态符号。
|
||||
|
||||
v0 不把 `mpv_handle`、`mpv_node`、FFmpeg、OHCodec、EGL、OHAudio 或 OHNativeWindow 类型带出 SDK。
|
||||
@@ -0,0 +1,27 @@
|
||||
# Velix Core SDK
|
||||
|
||||
Phase 7 SDK v0 以 `include/velix/core.h` 与 `libvelix_core_binding.so` 为产品边界,主 App 不编译 MPV/FFmpeg 源码。
|
||||
|
||||
## 目录
|
||||
|
||||
```text
|
||||
velix-core/
|
||||
├── include/velix/core.h
|
||||
├── include/mpv/harmony_playback.h
|
||||
├── lib/arm64-v8a/libvelix_core_binding.so
|
||||
├── lib/arm64-v8a/libmpv.so.2
|
||||
├── lib/arm64-v8a/libc++_shared.so
|
||||
├── metadata/build-info.json
|
||||
└── licenses/
|
||||
```
|
||||
|
||||
`libvelix_core_binding.so` 仅提供产品生命周期、控制、状态、媒体、音轨、字幕、指标与 build-info NAPI;XComponent 的 OHNativeWindow/EGL/mpv render context 仍由 Core 内部实现。主 App 只能看到 `PlaybackEngine`/`PlaybackController` 的业务类型。
|
||||
|
||||
默认 profile 为 software decode,OHCodec GL mapper 保持关闭。SDK 产物位于 `dist/velix-core`,已在 `.gitignore` 中忽略,禁止把 34MB `libmpv` 二进制提交到 Git。
|
||||
|
||||
构建与 staging:
|
||||
|
||||
```sh
|
||||
./scripts/package-velix-core-sdk.sh --profile default
|
||||
../Velix/scripts/build-velix-app.sh --release-core --build-mode debug
|
||||
```
|
||||
@@ -0,0 +1,13 @@
|
||||
# PlaybackEngine
|
||||
|
||||
`PlaybackEngine` 是主 App 的唯一播放抽象,`MpvPlaybackEngine` 是 Core binding 的适配器,`PlaybackController` 负责页面状态刷新与生命周期编排。
|
||||
|
||||
业务层只使用:
|
||||
|
||||
- `PlaybackSource { kind, url, headers }`
|
||||
- `PlaybackState { state, position, duration, media, tracks, metrics }`
|
||||
- play/pause/stop/release、seek、音量/倍速、音轨/字幕操作
|
||||
|
||||
provider 接口暂不绑定 Emby、Jellyfin、百度网盘等服务。HTTP(S) 源支持有限 headers;复杂 header 编辑器、登录与播放列表不在 v0 范围。
|
||||
|
||||
默认解码策略为软件解码;`requestedDecoder=software` 是主 App 的固定默认值,OHCodec GL mapper 不在本阶段启用。
|
||||
@@ -0,0 +1,14 @@
|
||||
# Player lifecycle
|
||||
|
||||
产品调用顺序:
|
||||
|
||||
```text
|
||||
create → attachSurface → prepare(source) → play
|
||||
↘ pause / seekTo / seekBy / tracks / subtitles
|
||||
stop → prepare 或 release
|
||||
detachSurface → release
|
||||
```
|
||||
|
||||
`onForeground`、`onBackground`、`onSurfaceAvailable`、`onSurfaceDestroyed` 是显式适配接口。当前后台策略只释放/暂停 Surface 相关资源,不承诺后台播放、PiP 或通知栏控制。Surface 与 player 生命周期解耦:Surface 销毁不会自动销毁 Core player,新的 Surface 可重新 attach。
|
||||
|
||||
页面状态以 500ms 轮询读取,错误通过 `errorCode` 与 `lastError` 映射到 UI;首帧、缓冲、seek、音频与 renderer 指标仅在“显示调试统计”打开时显示。
|
||||
@@ -0,0 +1,20 @@
|
||||
# SDK packaging
|
||||
|
||||
使用 `scripts/package-velix-core-sdk.sh` 生成 staging 目录。脚本按 profile 选择已构建的 `libmpv`,编译 binding,复制公开 headers、运行库、许可证,并生成包含 API 版本、commit、profile、尺寸和 SHA-256 的 `metadata/build-info.json`。
|
||||
|
||||
支持 profile:
|
||||
|
||||
```sh
|
||||
./scripts/package-velix-core-sdk.sh --profile default
|
||||
./scripts/package-velix-core-sdk.sh --profile software
|
||||
./scripts/package-velix-core-sdk.sh --profile ffmpeg-harmony
|
||||
```
|
||||
|
||||
ABI 检查重点:
|
||||
|
||||
- `libvelix_core_binding.so` 只导出 3 个 `velix_core_get_*` ABI 符号;NAPI 模块描述由系统注册段加载,不把内部 C++ 符号作为 ABI;
|
||||
- `libmpv.so.2` 保持自身运行时导出,不将其头文件类型传给主 App;
|
||||
- 所有动态依赖必须存在于 `lib/arm64-v8a` 或 OpenHarmony 系统库中;
|
||||
- staging 与构建目录不应进入 Git,发布包必须保留 LGPL/GPL/第三方声明。
|
||||
|
||||
验证:`./scripts/verify-velix-core-sdk.sh`。
|
||||
@@ -0,0 +1,10 @@
|
||||
# Velix 主 App 集成
|
||||
|
||||
主 App 工程为 `../Velix`,当前是独立的 Stage Model/API 24 工程,目录本身没有 Git 元数据。集成脚本提供两种依赖模式:
|
||||
|
||||
- `scripts/sync-velix-core-sdk.sh --local`:从相邻 Core 重新构建并 staging,适合本地开发。
|
||||
- `scripts/sync-velix-core-sdk.sh --release --sdk PATH`:消费已经发布的 SDK 目录,适合 release 验证。
|
||||
|
||||
`scripts/build-velix-app.sh` 会先同步 SDK,再执行 `devecocli build`。主 App 只将 `libvelix_core_binding.so`、`libmpv.so.2` 与 `libc++_shared.so` 放进 `entry/libs/arm64-v8a`,不引入 Core/FFmpeg 源码。
|
||||
|
||||
ArkTS 入口为 `domain/playback/PlaybackEngine.ets` → `MpvPlaybackEngine.ets` → native binding;页面为 `pages/PlayerPage.ets`。XComponent 的 `libraryname` 为 `velix_core_binding`,Surface 回调由 Core binding 注册,页面不重复实现 EGL renderer。
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define VELIX_CORE_API __attribute__((visibility("default")))
|
||||
#else
|
||||
#define VELIX_CORE_API
|
||||
#endif
|
||||
|
||||
#define VELIX_CORE_API_VERSION 0x000100u
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct velix_core_build_info {
|
||||
uint32_t api_version;
|
||||
const char* sdk_version;
|
||||
const char* mpv_version;
|
||||
const char* mpv_commit;
|
||||
const char* ffmpeg_commit;
|
||||
const char* ffmpeg_harmony_commit;
|
||||
const char* build_date;
|
||||
const char* target;
|
||||
const char* architecture;
|
||||
const char* profile;
|
||||
const char* feature_flags;
|
||||
} velix_core_build_info;
|
||||
|
||||
VELIX_CORE_API uint32_t velix_core_get_api_version(void);
|
||||
VELIX_CORE_API const char* velix_core_get_version(void);
|
||||
VELIX_CORE_API const velix_core_build_info* velix_core_get_build_info(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
repo_root=$(CDPATH= cd -- "$(dirname -- "$0")/../.." && pwd)
|
||||
repo_root=$(CDPATH= cd -- "${0%/*}/../.." && pwd)
|
||||
source_lib="$repo_root/build/ohos-arm64/prefix/lib/libmpv.so.2.5.0"
|
||||
target_dir="$repo_root/playback_lab/entry/libs/arm64-v8a"
|
||||
target_lib="$target_dir/libmpv.so.2"
|
||||
@@ -11,6 +11,6 @@ if [ ! -f "$source_lib" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$target_dir"
|
||||
cp -f "$source_lib" "$target_lib"
|
||||
echo "staged $target_lib ($(stat -f %z "$target_lib") bytes)"
|
||||
/bin/mkdir -p "$target_dir"
|
||||
/bin/cp -f "$source_lib" "$target_lib"
|
||||
echo "staged $target_lib ($(/usr/bin/stat -f %z "$target_lib") bytes)"
|
||||
Executable
+106
@@ -0,0 +1,106 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
PROFILE=default
|
||||
OUTPUT_DIR=${VELIX_CORE_SDK_OUT:-$PROJECT_ROOT/dist/velix-core}
|
||||
|
||||
while test "$#" -gt 0; do
|
||||
case "$1" in
|
||||
--profile)
|
||||
test "$#" -ge 2 || { echo "missing --profile value" >&2; exit 2; }
|
||||
PROFILE=$2
|
||||
shift 2
|
||||
;;
|
||||
--out)
|
||||
test "$#" -ge 2 || { echo "missing --out value" >&2; exit 2; }
|
||||
OUTPUT_DIR=$2
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
echo "usage: $0 [--profile default|software|ffmpeg-harmony] [--out DIR]" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
case "$PROFILE" in
|
||||
default)
|
||||
PROFILE_NAME=default-software
|
||||
MPV_LIBRARY="$PROJECT_ROOT/build/ohos-arm64/prefix/lib/libmpv.so.2.5.0"
|
||||
BUILD_SCRIPT="$PROJECT_ROOT/scripts/build-mpv-ohos.sh"
|
||||
;;
|
||||
software)
|
||||
PROFILE_NAME=software
|
||||
MPV_LIBRARY="$PROJECT_ROOT/build/ohos-arm64/build/mpv-software/libmpv.so.2.5.0"
|
||||
BUILD_SCRIPT="$PROJECT_ROOT/scripts/build-mpv-ohos-software.sh"
|
||||
;;
|
||||
ffmpeg-harmony)
|
||||
PROFILE_NAME=ffmpeg-harmony
|
||||
MPV_LIBRARY="$PROJECT_ROOT/build/ohos-arm64/build/mpv-ffmpeg-harmony/libmpv.so.2.5.0"
|
||||
BUILD_SCRIPT="$PROJECT_ROOT/scripts/build-mpv-ohos-ffmpeg-harmony.sh"
|
||||
;;
|
||||
*)
|
||||
echo "unsupported profile: $PROFILE" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
if test ! -f "$MPV_LIBRARY"; then
|
||||
"$BUILD_SCRIPT"
|
||||
fi
|
||||
test -f "$MPV_LIBRARY" || { echo "missing libmpv: $MPV_LIBRARY" >&2; exit 1; }
|
||||
|
||||
. "$PROJECT_ROOT/scripts/setup-ohos-env.sh"
|
||||
SDK_BUILD="$OHOS_BUILD_ROOT/build/velix-core-sdk-${PROFILE}"
|
||||
cmake -S "$PROJECT_ROOT/sdk/native" -B "$SDK_BUILD" -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_C_COMPILER="$CC" -DCMAKE_CXX_COMPILER="$CXX" \
|
||||
-DCMAKE_AR="$AR" -DCMAKE_RANLIB="$RANLIB" -DCMAKE_STRIP="$STRIP" \
|
||||
-DCMAKE_SYSTEM_NAME=OHOS -DCMAKE_SYSTEM_PROCESSOR=aarch64 \
|
||||
-DCMAKE_C_COMPILER_TARGET="$TARGET" -DCMAKE_CXX_COMPILER_TARGET="$TARGET" \
|
||||
-DCMAKE_SYSROOT="$OHOS_SYSROOT" -DCMAKE_C_FLAGS="$OHOS_COMMON_FLAGS" \
|
||||
-DCMAKE_CXX_FLAGS="$OHOS_COMMON_FLAGS" \
|
||||
-DMPV_LIBRARY="$MPV_LIBRARY" -DVELIX_CORE_PROFILE_NAME="$PROFILE_NAME"
|
||||
cmake --build "$SDK_BUILD" -j2
|
||||
|
||||
rm -rf "$OUTPUT_DIR"
|
||||
mkdir -p "$OUTPUT_DIR/include/velix" "$OUTPUT_DIR/include/mpv" "$OUTPUT_DIR/lib/arm64-v8a" "$OUTPUT_DIR/metadata" "$OUTPUT_DIR/licenses"
|
||||
cp "$PROJECT_ROOT/include/velix/core.h" "$OUTPUT_DIR/include/velix/core.h"
|
||||
cp "$PROJECT_ROOT/include/mpv/harmony_playback.h" "$OUTPUT_DIR/include/mpv/harmony_playback.h"
|
||||
cp "$SDK_BUILD/libvelix_core_binding.so" "$OUTPUT_DIR/lib/arm64-v8a/libvelix_core_binding.so"
|
||||
cp "$MPV_LIBRARY" "$OUTPUT_DIR/lib/arm64-v8a/libmpv.so.2"
|
||||
|
||||
RUNTIME_LIB=$(find "$PROJECT_ROOT/playback_lab/entry/build" -type f -name 'libc++_shared.so' -print -quit 2>/dev/null || true)
|
||||
if test -n "$RUNTIME_LIB"; then
|
||||
cp "$RUNTIME_LIB" "$OUTPUT_DIR/lib/arm64-v8a/libc++_shared.so"
|
||||
fi
|
||||
|
||||
cp "$PROJECT_ROOT/LICENSE.LGPL" "$OUTPUT_DIR/licenses/LICENSE.LGPL"
|
||||
cp "$PROJECT_ROOT/LICENSE.GPL" "$OUTPUT_DIR/licenses/LICENSE.GPL"
|
||||
cp "$PROJECT_ROOT/Copyright" "$OUTPUT_DIR/licenses/Copyright"
|
||||
cp "$PROJECT_ROOT/THIRD_PARTY_NOTICES.md" "$OUTPUT_DIR/licenses/THIRD_PARTY_NOTICES.md"
|
||||
|
||||
binding_size=$(stat -f %z "$OUTPUT_DIR/lib/arm64-v8a/libvelix_core_binding.so")
|
||||
mpv_size=$(stat -f %z "$OUTPUT_DIR/lib/arm64-v8a/libmpv.so.2")
|
||||
binding_sha=$(shasum -a 256 "$OUTPUT_DIR/lib/arm64-v8a/libvelix_core_binding.so" | awk '{print $1}')
|
||||
mpv_sha=$(shasum -a 256 "$OUTPUT_DIR/lib/arm64-v8a/libmpv.so.2" | awk '{print $1}')
|
||||
cat > "$OUTPUT_DIR/metadata/build-info.json" <<EOF
|
||||
{
|
||||
"sdkVersion": "7.0.0",
|
||||
"apiVersion": "0x000100",
|
||||
"profile": "$PROFILE_NAME",
|
||||
"target": "OpenHarmony API 24",
|
||||
"architecture": "arm64-v8a",
|
||||
"mpvVersion": "0.41.0-UNKNOWN",
|
||||
"mpvCommit": "e594f66c35d5eb51ecb44895ceacd1bffa469db2",
|
||||
"ffmpegHarmonyCommit": "f3a036c695bcde37407b48a84f3d75e827a792a7",
|
||||
"features": ["software-decode", "opengl", "ohos-audio", "http", "subtitle"],
|
||||
"libraries": [
|
||||
{"name": "libvelix_core_binding.so", "size": $binding_size, "sha256": "$binding_sha"},
|
||||
{"name": "libmpv.so.2", "size": $mpv_size, "sha256": "$mpv_sha"}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
printf 'SDK=%s\nPROFILE=%s\nBINDING=%s bytes\nMPV=%s bytes\n' "$OUTPUT_DIR" "$PROFILE_NAME" "$binding_size" "$mpv_size"
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
SDK_ROOT=${1:-$PROJECT_ROOT/dist/velix-core}
|
||||
BIN_DIR="$SDK_ROOT/lib/arm64-v8a"
|
||||
for file in "$SDK_ROOT/include/velix/core.h" "$SDK_ROOT/include/mpv/harmony_playback.h" \
|
||||
"$BIN_DIR/libvelix_core_binding.so" "$BIN_DIR/libmpv.so.2" "$SDK_ROOT/metadata/build-info.json"; do
|
||||
test -f "$file" || { echo "missing SDK file: $file" >&2; exit 1; }
|
||||
done
|
||||
|
||||
. "$PROJECT_ROOT/scripts/setup-ohos-env.sh"
|
||||
"$PROJECT_ROOT/scripts/verify-ohos-binary.sh" "$BIN_DIR/libvelix_core_binding.so" "$BIN_DIR/libmpv.so.2" >/dev/null
|
||||
symbols=$($NM -D --defined-only "$BIN_DIR/libvelix_core_binding.so")
|
||||
for symbol in velix_core_get_api_version velix_core_get_version velix_core_get_build_info; do
|
||||
printf '%s\n' "$symbols" | /usr/bin/grep -q " $symbol$" || { echo "missing exported symbol: $symbol" >&2; exit 1; }
|
||||
done
|
||||
public_count=$(printf '%s\n' "$symbols" | /usr/bin/awk '$3 !~ /^(_fini|_init)$/ {count++} END {print count + 0}')
|
||||
test "$public_count" -eq 3 || { echo "unexpected Velix public symbol count: $public_count" >&2; exit 1; }
|
||||
printf 'SDK verified: %s (Velix ABI exports=%s)\n' "$SDK_ROOT" "$public_count"
|
||||
@@ -0,0 +1,39 @@
|
||||
cmake_minimum_required(VERSION 3.5.0)
|
||||
project(velix_core_sdk)
|
||||
|
||||
set(CORE_ROOT "${CMAKE_CURRENT_LIST_DIR}/../..")
|
||||
set(MPV_LIBRARY "${CORE_ROOT}/build/ohos-arm64/prefix/lib/libmpv.so.2.5.0" CACHE FILEPATH "Core libmpv path")
|
||||
set(VELIX_CORE_PROFILE_NAME "default-software" CACHE STRING "SDK build profile")
|
||||
if(NOT EXISTS "${MPV_LIBRARY}")
|
||||
message(FATAL_ERROR "Missing ${MPV_LIBRARY}; build Core before packaging the SDK")
|
||||
endif()
|
||||
|
||||
set(PLAYER_ROOT "${CORE_ROOT}/playback_lab/entry/src/main/cpp")
|
||||
include_directories(
|
||||
"${CORE_ROOT}/include"
|
||||
"${CORE_ROOT}"
|
||||
"${PLAYER_ROOT}"
|
||||
"${CORE_ROOT}/build/ohos-arm64/prefix/include"
|
||||
)
|
||||
|
||||
add_library(mpv SHARED IMPORTED GLOBAL)
|
||||
set_target_properties(mpv PROPERTIES IMPORTED_LOCATION "${MPV_LIBRARY}")
|
||||
|
||||
add_library(velix_core_binding SHARED
|
||||
product_binding.cpp
|
||||
core_build_info.cpp
|
||||
"${PLAYER_ROOT}/player.cpp"
|
||||
"${PLAYER_ROOT}/render.cpp"
|
||||
)
|
||||
target_compile_features(velix_core_binding PRIVATE cxx_std_17)
|
||||
target_compile_options(velix_core_binding PRIVATE -fvisibility=hidden -Wall -Wextra -Werror=return-type)
|
||||
target_compile_definitions(velix_core_binding PRIVATE VELIX_CORE_PROFILE_NAME="${VELIX_CORE_PROFILE_NAME}")
|
||||
target_link_libraries(velix_core_binding PUBLIC
|
||||
mpv
|
||||
libace_napi.z.so
|
||||
libace_ndk.z.so
|
||||
libEGL.so
|
||||
libGLESv3.so
|
||||
libnative_window.so
|
||||
libhilog_ndk.z.so
|
||||
)
|
||||
@@ -0,0 +1,46 @@
|
||||
#include "velix/core.h"
|
||||
|
||||
namespace {
|
||||
constexpr char kSdkVersion[] = "7.0.0";
|
||||
constexpr char kMpvVersion[] = "0.41.0-UNKNOWN";
|
||||
constexpr char kMpvCommit[] = "e594f66c35d5eb51ecb44895ceacd1bffa469db2";
|
||||
constexpr char kFfmpegCommit[] = "f3a036c695bcde37407b48a84f3d75e827a792a7";
|
||||
constexpr char kFfmpegHarmonyCommit[] = "f3a036c695bcde37407b48a84f3d75e827a792a7";
|
||||
constexpr char kBuildDate[] = "2026-08-29";
|
||||
constexpr char kTarget[] = "OpenHarmony API 24";
|
||||
constexpr char kArchitecture[] = "arm64-v8a";
|
||||
#ifndef VELIX_CORE_PROFILE_NAME
|
||||
#define VELIX_CORE_PROFILE_NAME "default-software"
|
||||
#endif
|
||||
constexpr char kProfile[] = VELIX_CORE_PROFILE_NAME;
|
||||
constexpr char kFeatures[] = "software-decode,opengl,ohos-audio,http,subtitle";
|
||||
|
||||
constexpr velix_core_build_info kBuildInfo = {
|
||||
VELIX_CORE_API_VERSION,
|
||||
kSdkVersion,
|
||||
kMpvVersion,
|
||||
kMpvCommit,
|
||||
kFfmpegCommit,
|
||||
kFfmpegHarmonyCommit,
|
||||
kBuildDate,
|
||||
kTarget,
|
||||
kArchitecture,
|
||||
kProfile,
|
||||
kFeatures,
|
||||
};
|
||||
}
|
||||
|
||||
extern "C" VELIX_CORE_API uint32_t velix_core_get_api_version(void)
|
||||
{
|
||||
return VELIX_CORE_API_VERSION;
|
||||
}
|
||||
|
||||
extern "C" VELIX_CORE_API const char* velix_core_get_version(void)
|
||||
{
|
||||
return kSdkVersion;
|
||||
}
|
||||
|
||||
extern "C" VELIX_CORE_API const velix_core_build_info* velix_core_get_build_info(void)
|
||||
{
|
||||
return &kBuildInfo;
|
||||
}
|
||||
@@ -0,0 +1,579 @@
|
||||
#include "player.h"
|
||||
#include "render.h"
|
||||
#include "velix/core.h"
|
||||
|
||||
#include <ace/xcomponent/native_interface_xcomponent.h>
|
||||
#include <napi/native_api.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace {
|
||||
|
||||
PlaybackEngine gPlayer;
|
||||
EglRenderer gRenderer(gPlayer);
|
||||
|
||||
napi_value boolean(napi_env env, bool value)
|
||||
{
|
||||
napi_value result = nullptr;
|
||||
napi_get_boolean(env, value, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
void setString(napi_env env, napi_value object, const char* name, const std::string& value)
|
||||
{
|
||||
napi_value item = nullptr;
|
||||
napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &item);
|
||||
napi_set_named_property(env, object, name, item);
|
||||
}
|
||||
|
||||
void setString(napi_env env, napi_value object, const char* name, const char* value)
|
||||
{
|
||||
napi_value item = nullptr;
|
||||
napi_create_string_utf8(env, value == nullptr ? "" : value, NAPI_AUTO_LENGTH, &item);
|
||||
napi_set_named_property(env, object, name, item);
|
||||
}
|
||||
|
||||
void setDouble(napi_env env, napi_value object, const char* name, double value)
|
||||
{
|
||||
napi_value item = nullptr;
|
||||
napi_create_double(env, value, &item);
|
||||
napi_set_named_property(env, object, name, item);
|
||||
}
|
||||
|
||||
void setInt64(napi_env env, napi_value object, const char* name, int64_t value)
|
||||
{
|
||||
napi_value item = nullptr;
|
||||
napi_create_int64(env, value, &item);
|
||||
napi_set_named_property(env, object, name, item);
|
||||
}
|
||||
|
||||
void setBool(napi_env env, napi_value object, const char* name, bool value)
|
||||
{
|
||||
napi_value item = nullptr;
|
||||
napi_get_boolean(env, value, &item);
|
||||
napi_set_named_property(env, object, name, item);
|
||||
}
|
||||
|
||||
bool firstArg(napi_env env, napi_callback_info info, napi_value& value)
|
||||
{
|
||||
size_t argc = 1;
|
||||
napi_value argv[1] = {nullptr};
|
||||
if (napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr) != napi_ok || argc == 0) return false;
|
||||
value = argv[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
bool valueString(napi_env env, napi_value value, std::string& result)
|
||||
{
|
||||
size_t size = 0;
|
||||
if (napi_get_value_string_utf8(env, value, nullptr, 0, &size) != napi_ok) return false;
|
||||
result.resize(size + 1);
|
||||
if (napi_get_value_string_utf8(env, value, result.data(), result.size(), &size) != napi_ok) return false;
|
||||
result.resize(size);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool namedString(napi_env env, napi_value object, const char* name, std::string& result)
|
||||
{
|
||||
bool has = false;
|
||||
napi_value value = nullptr;
|
||||
return napi_has_named_property(env, object, name, &has) == napi_ok && has &&
|
||||
napi_get_named_property(env, object, name, &value) == napi_ok && valueString(env, value, result);
|
||||
}
|
||||
|
||||
bool namedDouble(napi_env env, napi_value object, const char* name, double& result)
|
||||
{
|
||||
bool has = false;
|
||||
napi_value value = nullptr;
|
||||
return napi_has_named_property(env, object, name, &has) == napi_ok && has &&
|
||||
napi_get_named_property(env, object, name, &value) == napi_ok &&
|
||||
napi_get_value_double(env, value, &result) == napi_ok;
|
||||
}
|
||||
|
||||
bool namedInt(napi_env env, napi_value object, const char* name, int64_t& result)
|
||||
{
|
||||
double number = 0.0;
|
||||
if (!namedDouble(env, object, name, number)) return false;
|
||||
result = static_cast<int64_t>(number);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool namedBool(napi_env env, napi_value object, const char* name, bool& result)
|
||||
{
|
||||
bool has = false;
|
||||
napi_value value = nullptr;
|
||||
return napi_has_named_property(env, object, name, &has) == napi_ok && has &&
|
||||
napi_get_named_property(env, object, name, &value) == napi_ok &&
|
||||
napi_get_value_bool(env, value, &result) == napi_ok;
|
||||
}
|
||||
|
||||
bool requestFromArg(napi_env env, napi_value argument, PlaybackRequestModel& request)
|
||||
{
|
||||
napi_valuetype type = napi_undefined;
|
||||
if (napi_typeof(env, argument, &type) != napi_ok) return false;
|
||||
if (type == napi_string) return valueString(env, argument, request.url) && !request.url.empty();
|
||||
if (type != napi_object || !namedString(env, argument, "url", request.url) || request.url.empty()) return false;
|
||||
namedString(env, argument, "title", request.title);
|
||||
namedString(env, argument, "userAgent", request.userAgent);
|
||||
namedString(env, argument, "referer", request.referer);
|
||||
namedString(env, argument, "cookies", request.cookies);
|
||||
int64_t number = -1;
|
||||
if (namedInt(env, argument, "startPositionMs", number)) request.startPositionMs = number;
|
||||
if (namedInt(env, argument, "audioTrackId", number)) request.audioTrackId = static_cast<int>(number);
|
||||
if (namedInt(env, argument, "subtitleTrackId", number)) request.subtitleTrackId = static_cast<int>(number);
|
||||
double decimal = 0.0;
|
||||
if (namedDouble(env, argument, "playbackSpeed", decimal)) request.playbackSpeed = decimal;
|
||||
if (namedDouble(env, argument, "networkTimeout", decimal)) request.networkTimeout = decimal;
|
||||
bool enabled = true;
|
||||
if (namedBool(env, argument, "subtitleEnabled", enabled)) request.subtitleEnabled = enabled ? 1 : 0;
|
||||
|
||||
napi_value headers = nullptr;
|
||||
bool has = false;
|
||||
if (napi_has_named_property(env, argument, "httpHeaders", &has) == napi_ok && has &&
|
||||
napi_get_named_property(env, argument, "httpHeaders", &headers) == napi_ok) {
|
||||
uint32_t length = 0;
|
||||
if (napi_get_array_length(env, headers, &length) == napi_ok) {
|
||||
for (uint32_t index = 0; index < length && index < 32; ++index) {
|
||||
napi_value item = nullptr;
|
||||
PlaybackHeader header;
|
||||
if (napi_get_element(env, headers, index, &item) == napi_ok &&
|
||||
namedString(env, item, "name", header.name) && namedString(env, item, "value", header.value)) {
|
||||
request.headers.push_back(std::move(header));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void serializeTrack(napi_env env, napi_value object, const PlaybackTrack& track)
|
||||
{
|
||||
setInt64(env, object, "id", track.id);
|
||||
setString(env, object, "type", track.type);
|
||||
setString(env, object, "title", track.title);
|
||||
setString(env, object, "language", track.language);
|
||||
setString(env, object, "codec", track.codec);
|
||||
setBool(env, object, "default", track.isDefault);
|
||||
setBool(env, object, "forced", track.forced);
|
||||
setBool(env, object, "selected", track.selected);
|
||||
setBool(env, object, "external", track.external);
|
||||
}
|
||||
|
||||
napi_value tracksValue(napi_env env, const std::vector<PlaybackTrack>& tracks)
|
||||
{
|
||||
napi_value result = nullptr;
|
||||
napi_create_array_with_length(env, tracks.size(), &result);
|
||||
for (size_t index = 0; index < tracks.size(); ++index) {
|
||||
napi_value item = nullptr;
|
||||
napi_create_object(env, &item);
|
||||
serializeTrack(env, item, tracks[index]);
|
||||
napi_set_element(env, result, index, item);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
napi_value mediaValue(napi_env env, const PlaybackMediaInfo& media)
|
||||
{
|
||||
napi_value result = nullptr;
|
||||
napi_create_object(env, &result);
|
||||
setString(env, result, "container", media.container);
|
||||
setString(env, result, "demuxer", media.demuxer);
|
||||
setDouble(env, result, "duration", media.duration);
|
||||
setString(env, result, "videoCodec", media.videoCodec);
|
||||
setString(env, result, "audioCodec", media.audioCodec);
|
||||
setString(env, result, "pixelFormat", media.pixelFormat);
|
||||
setString(env, result, "colorPrimaries", media.colorPrimaries);
|
||||
setString(env, result, "colorTransfer", media.colorTransfer);
|
||||
setString(env, result, "colorSpace", media.colorSpace);
|
||||
setString(env, result, "colorMatrix", media.colorMatrix);
|
||||
setInt64(env, result, "width", media.width);
|
||||
setInt64(env, result, "height", media.height);
|
||||
setInt64(env, result, "videoWidth", media.videoWidth);
|
||||
setInt64(env, result, "videoHeight", media.videoHeight);
|
||||
setInt64(env, result, "bitDepth", media.bitDepth);
|
||||
setInt64(env, result, "audioSampleRate", media.audioSampleRate);
|
||||
setInt64(env, result, "audioChannels", media.audioChannels);
|
||||
setString(env, result, "audioChannelLayout", media.audioChannelLayout);
|
||||
setDouble(env, result, "fps", media.fps);
|
||||
setInt64(env, result, "bitrate", media.bitrate);
|
||||
setInt64(env, result, "videoBitrate", media.videoBitrate);
|
||||
setInt64(env, result, "audioBitrate", media.audioBitrate);
|
||||
setInt64(env, result, "trackCount", media.trackCount);
|
||||
setInt64(env, result, "subtitleCount", media.subtitleCount);
|
||||
return result;
|
||||
}
|
||||
|
||||
napi_value metricsValue(napi_env env, const PlaybackSnapshot& state)
|
||||
{
|
||||
napi_value result = nullptr;
|
||||
napi_create_object(env, &result);
|
||||
setInt64(env, result, "droppedFrames", state.dropped);
|
||||
setInt64(env, result, "renderCount", static_cast<int64_t>(state.renderCount));
|
||||
setBool(env, result, "buffering", state.network.buffering);
|
||||
setInt64(env, result, "bufferingEnterCount", state.metrics.buffering_enter_count);
|
||||
setInt64(env, result, "bufferingExitCount", state.metrics.buffering_exit_count);
|
||||
setInt64(env, result, "bufferingTotalMs", state.metrics.buffering_total_ms);
|
||||
setInt64(env, result, "bufferingMaxSingleMs", state.metrics.buffering_max_single_ms);
|
||||
setInt64(env, result, "networkWaitMs", state.metrics.network_wait_ms);
|
||||
setInt64(env, result, "lateFrames", state.metrics.late_frames);
|
||||
setInt64(env, result, "mistimedFrames", state.metrics.mistimed_frames);
|
||||
setInt64(env, result, "decoderFrames", state.metrics.decoder_frames);
|
||||
setInt64(env, result, "cacheBytes", state.network.cacheBytes);
|
||||
setDouble(env, result, "cacheDuration", state.network.cacheDuration);
|
||||
setInt64(env, result, "currentBitrate", state.network.currentBitrate);
|
||||
return result;
|
||||
}
|
||||
|
||||
napi_value stateValue(napi_env env)
|
||||
{
|
||||
const PlaybackSnapshot state = gPlayer.snapshot();
|
||||
napi_value result = nullptr;
|
||||
napi_create_object(env, &result);
|
||||
setString(env, result, "state", state.state);
|
||||
setInt64(env, result, "sessionId", static_cast<int64_t>(state.sessionId));
|
||||
setDouble(env, result, "position", state.position);
|
||||
setDouble(env, result, "duration", state.duration);
|
||||
setDouble(env, result, "fps", state.fps);
|
||||
setInt64(env, result, "dropped", state.dropped);
|
||||
setInt64(env, result, "firstFrameMs", state.firstFrameMs);
|
||||
setInt64(env, result, "renderCount", static_cast<int64_t>(state.renderCount));
|
||||
setString(env, result, "lastError", state.lastError);
|
||||
setString(env, result, "errorCode", state.errorCode);
|
||||
setDouble(env, result, "speed", state.speed);
|
||||
setDouble(env, result, "volume", state.volume);
|
||||
setBool(env, result, "muted", state.muted);
|
||||
setInt64(env, result, "subtitleDelayMs", state.subtitleDelayMs);
|
||||
setDouble(env, result, "subtitleScale", state.subtitleScale);
|
||||
setDouble(env, result, "subtitlePosition", state.subtitlePosition);
|
||||
setString(env, result, "requestedDecoder", state.requestedDecoder);
|
||||
setString(env, result, "actualDecoder", state.actualDecoder);
|
||||
setString(env, result, "hwCapability", state.hwCapability);
|
||||
setBool(env, result, "hwdecRequested", state.hwdecRequested);
|
||||
setBool(env, result, "hwdecActive", state.hwdecActive);
|
||||
setInt64(env, result, "hardwareFrameCount", state.hardwareFrameCount);
|
||||
setInt64(env, result, "softwareFrameCount", state.softwareFrameCount);
|
||||
setInt64(env, result, "hwFallbackCount", state.hwFallbackCount);
|
||||
setInt64(env, result, "nativeBufferImportCount", state.nativeBufferImportCount);
|
||||
setInt64(env, result, "nativeBufferImportFailure", state.nativeBufferImportFailure);
|
||||
setInt64(env, result, "surfacePresentCount", state.surfacePresentCount);
|
||||
setInt64(env, result, "gpuInteropFrameCount", state.gpuInteropFrameCount);
|
||||
setString(env, result, "surface", gRenderer.surfaceStatus());
|
||||
setString(env, result, "egl", gRenderer.eglStatus());
|
||||
setString(env, result, "mpv", gRenderer.mpvStatus());
|
||||
napi_set_named_property(env, result, "media", mediaValue(env, state.media));
|
||||
napi_set_named_property(env, result, "tracks", tracksValue(env, state.tracks));
|
||||
napi_set_named_property(env, result, "metrics", metricsValue(env, state));
|
||||
return result;
|
||||
}
|
||||
|
||||
napi_value createPlayer(napi_env env, napi_callback_info)
|
||||
{
|
||||
return boolean(env, gPlayer.initialize());
|
||||
}
|
||||
|
||||
napi_value prepare(napi_env env, napi_callback_info info)
|
||||
{
|
||||
napi_value argument = nullptr;
|
||||
PlaybackRequestModel request;
|
||||
return firstArg(env, info, argument) && requestFromArg(env, argument, request)
|
||||
? boolean(env, gPlayer.load(request))
|
||||
: boolean(env, false);
|
||||
}
|
||||
|
||||
napi_value play(napi_env env, napi_callback_info)
|
||||
{
|
||||
gPlayer.play();
|
||||
return boolean(env, true);
|
||||
}
|
||||
|
||||
napi_value pause(napi_env env, napi_callback_info)
|
||||
{
|
||||
gPlayer.pause();
|
||||
return boolean(env, true);
|
||||
}
|
||||
|
||||
napi_value stop(napi_env env, napi_callback_info)
|
||||
{
|
||||
gPlayer.stop();
|
||||
return boolean(env, true);
|
||||
}
|
||||
|
||||
napi_value release(napi_env env, napi_callback_info)
|
||||
{
|
||||
gRenderer.onSurfaceDestroyed(nullptr);
|
||||
gPlayer.release();
|
||||
return boolean(env, true);
|
||||
}
|
||||
|
||||
napi_value attachSurface(napi_env env, napi_callback_info)
|
||||
{
|
||||
gRenderer.requestRender();
|
||||
return boolean(env, gRenderer.hasSurface());
|
||||
}
|
||||
|
||||
napi_value detachSurface(napi_env env, napi_callback_info)
|
||||
{
|
||||
gRenderer.onSurfaceDestroyed(nullptr);
|
||||
return boolean(env, true);
|
||||
}
|
||||
|
||||
napi_value onForeground(napi_env env, napi_callback_info)
|
||||
{
|
||||
gRenderer.requestRender();
|
||||
return boolean(env, true);
|
||||
}
|
||||
|
||||
napi_value onBackground(napi_env env, napi_callback_info)
|
||||
{
|
||||
return boolean(env, true);
|
||||
}
|
||||
|
||||
napi_value onSurfaceAvailable(napi_env env, napi_callback_info)
|
||||
{
|
||||
gRenderer.requestRender();
|
||||
return boolean(env, true);
|
||||
}
|
||||
|
||||
napi_value onSurfaceDestroyed(napi_env env, napi_callback_info)
|
||||
{
|
||||
gRenderer.onSurfaceDestroyed(nullptr);
|
||||
return boolean(env, true);
|
||||
}
|
||||
|
||||
napi_value seekTo(napi_env env, napi_callback_info info)
|
||||
{
|
||||
napi_value argument = nullptr;
|
||||
double milliseconds = 0.0;
|
||||
if (!firstArg(env, info, argument) || napi_get_value_double(env, argument, &milliseconds) != napi_ok) {
|
||||
return boolean(env, false);
|
||||
}
|
||||
return boolean(env, gPlayer.seekAbsolute(milliseconds / 1000.0, false, false));
|
||||
}
|
||||
|
||||
napi_value seekBy(napi_env env, napi_callback_info info)
|
||||
{
|
||||
napi_value argument = nullptr;
|
||||
double milliseconds = 0.0;
|
||||
if (!firstArg(env, info, argument) || napi_get_value_double(env, argument, &milliseconds) != napi_ok) {
|
||||
return boolean(env, false);
|
||||
}
|
||||
gPlayer.seek(milliseconds / 1000.0);
|
||||
return boolean(env, true);
|
||||
}
|
||||
|
||||
napi_value setSpeed(napi_env env, napi_callback_info info)
|
||||
{
|
||||
napi_value argument = nullptr;
|
||||
double value = 1.0;
|
||||
return firstArg(env, info, argument) && napi_get_value_double(env, argument, &value) == napi_ok
|
||||
? boolean(env, gPlayer.setSpeed(value))
|
||||
: boolean(env, false);
|
||||
}
|
||||
|
||||
napi_value setVolume(napi_env env, napi_callback_info info)
|
||||
{
|
||||
napi_value argument = nullptr;
|
||||
double value = 100.0;
|
||||
return firstArg(env, info, argument) && napi_get_value_double(env, argument, &value) == napi_ok
|
||||
? boolean(env, gPlayer.setVolume(value))
|
||||
: boolean(env, false);
|
||||
}
|
||||
|
||||
napi_value setMute(napi_env env, napi_callback_info info)
|
||||
{
|
||||
napi_value argument = nullptr;
|
||||
bool value = false;
|
||||
return firstArg(env, info, argument) && napi_get_value_bool(env, argument, &value) == napi_ok
|
||||
? boolean(env, gPlayer.setMute(value))
|
||||
: boolean(env, false);
|
||||
}
|
||||
|
||||
napi_value selectAudioTrack(napi_env env, napi_callback_info info)
|
||||
{
|
||||
napi_value argument = nullptr;
|
||||
double value = -1;
|
||||
return firstArg(env, info, argument) && napi_get_value_double(env, argument, &value) == napi_ok
|
||||
? boolean(env, gPlayer.selectAudioTrack(static_cast<int>(value)))
|
||||
: boolean(env, false);
|
||||
}
|
||||
|
||||
napi_value selectSubtitleTrack(napi_env env, napi_callback_info info)
|
||||
{
|
||||
napi_value argument = nullptr;
|
||||
double value = -1;
|
||||
return firstArg(env, info, argument) && napi_get_value_double(env, argument, &value) == napi_ok
|
||||
? boolean(env, gPlayer.selectSubtitleTrack(static_cast<int>(value)))
|
||||
: boolean(env, false);
|
||||
}
|
||||
|
||||
napi_value disableSubtitle(napi_env env, napi_callback_info)
|
||||
{
|
||||
return boolean(env, gPlayer.disableSubtitle());
|
||||
}
|
||||
|
||||
napi_value addSubtitle(napi_env env, napi_callback_info info)
|
||||
{
|
||||
napi_value argument = nullptr;
|
||||
if (!firstArg(env, info, argument)) return boolean(env, false);
|
||||
PlaybackSubtitleSource source;
|
||||
napi_valuetype type = napi_undefined;
|
||||
if (napi_typeof(env, argument, &type) != napi_ok) return boolean(env, false);
|
||||
if (type == napi_string) {
|
||||
if (!valueString(env, argument, source.url)) return boolean(env, false);
|
||||
} else if (type == napi_object) {
|
||||
namedString(env, argument, "url", source.url);
|
||||
namedString(env, argument, "title", source.title);
|
||||
namedString(env, argument, "language", source.language);
|
||||
namedString(env, argument, "encoding", source.encoding);
|
||||
namedBool(env, argument, "forced", source.forced);
|
||||
namedBool(env, argument, "select", source.select);
|
||||
}
|
||||
return boolean(env, !source.url.empty() && gPlayer.addSubtitle(source));
|
||||
}
|
||||
|
||||
napi_value removeSubtitle(napi_env env, napi_callback_info info)
|
||||
{
|
||||
napi_value argument = nullptr;
|
||||
double value = -1;
|
||||
return firstArg(env, info, argument) && napi_get_value_double(env, argument, &value) == napi_ok
|
||||
? boolean(env, gPlayer.removeSubtitle(static_cast<int>(value)))
|
||||
: boolean(env, false);
|
||||
}
|
||||
|
||||
napi_value setSubtitleDelay(napi_env env, napi_callback_info info)
|
||||
{
|
||||
napi_value argument = nullptr;
|
||||
double value = 0.0;
|
||||
return firstArg(env, info, argument) && napi_get_value_double(env, argument, &value) == napi_ok
|
||||
? boolean(env, gPlayer.setSubtitleDelay(value))
|
||||
: boolean(env, false);
|
||||
}
|
||||
|
||||
napi_value setSubtitleScale(napi_env env, napi_callback_info info)
|
||||
{
|
||||
napi_value argument = nullptr;
|
||||
double value = 1.0;
|
||||
return firstArg(env, info, argument) && napi_get_value_double(env, argument, &value) == napi_ok
|
||||
? boolean(env, gPlayer.setSubtitleScale(value))
|
||||
: boolean(env, false);
|
||||
}
|
||||
|
||||
napi_value setSubtitlePosition(napi_env env, napi_callback_info info)
|
||||
{
|
||||
napi_value argument = nullptr;
|
||||
double value = 100.0;
|
||||
return firstArg(env, info, argument) && napi_get_value_double(env, argument, &value) == napi_ok
|
||||
? boolean(env, gPlayer.setSubtitlePosition(value))
|
||||
: boolean(env, false);
|
||||
}
|
||||
|
||||
napi_value getState(napi_env env, napi_callback_info)
|
||||
{
|
||||
return stateValue(env);
|
||||
}
|
||||
|
||||
napi_value getMediaInfo(napi_env env, napi_callback_info)
|
||||
{
|
||||
return mediaValue(env, gPlayer.snapshot().media);
|
||||
}
|
||||
|
||||
napi_value getTracks(napi_env env, napi_callback_info)
|
||||
{
|
||||
return tracksValue(env, gPlayer.snapshot().tracks);
|
||||
}
|
||||
|
||||
napi_value getMetrics(napi_env env, napi_callback_info)
|
||||
{
|
||||
return metricsValue(env, gPlayer.snapshot());
|
||||
}
|
||||
|
||||
napi_value getBuildInfo(napi_env env, napi_callback_info)
|
||||
{
|
||||
const velix_core_build_info* info = velix_core_get_build_info();
|
||||
napi_value result = nullptr;
|
||||
napi_create_object(env, &result);
|
||||
setInt64(env, result, "apiVersion", info->api_version);
|
||||
setString(env, result, "sdkVersion", info->sdk_version);
|
||||
setString(env, result, "mpvVersion", info->mpv_version);
|
||||
setString(env, result, "mpvCommit", info->mpv_commit);
|
||||
setString(env, result, "ffmpegCommit", info->ffmpeg_commit);
|
||||
setString(env, result, "ffmpegHarmonyCommit", info->ffmpeg_harmony_commit);
|
||||
setString(env, result, "buildDate", info->build_date);
|
||||
setString(env, result, "target", info->target);
|
||||
setString(env, result, "architecture", info->architecture);
|
||||
setString(env, result, "profile", info->profile);
|
||||
setString(env, result, "featureFlags", info->feature_flags);
|
||||
return result;
|
||||
}
|
||||
|
||||
void onSurfaceCreated(OH_NativeXComponent* component, void* window)
|
||||
{
|
||||
gRenderer.onSurfaceCreated(component, window);
|
||||
}
|
||||
|
||||
void onSurfaceChanged(OH_NativeXComponent* component, void* window)
|
||||
{
|
||||
gRenderer.onSurfaceChanged(component, window);
|
||||
}
|
||||
|
||||
void onSurfaceDestroyedCallback(OH_NativeXComponent*, void* window)
|
||||
{
|
||||
gRenderer.onSurfaceDestroyed(window);
|
||||
}
|
||||
|
||||
void onTouch(OH_NativeXComponent*, void*) {}
|
||||
|
||||
napi_value Init(napi_env env, napi_value exports)
|
||||
{
|
||||
gPlayer.initialize();
|
||||
napi_value xcomponent = nullptr;
|
||||
if (napi_get_named_property(env, exports, OH_NATIVE_XCOMPONENT_OBJ, &xcomponent) == napi_ok) {
|
||||
OH_NativeXComponent* component = nullptr;
|
||||
if (napi_unwrap(env, xcomponent, reinterpret_cast<void**>(&component)) == napi_ok && component != nullptr) {
|
||||
static OH_NativeXComponent_Callback callbacks = {
|
||||
onSurfaceCreated, onSurfaceChanged, onSurfaceDestroyedCallback, onTouch
|
||||
};
|
||||
OH_NativeXComponent_RegisterCallback(component, &callbacks);
|
||||
}
|
||||
}
|
||||
napi_property_descriptor properties[] = {
|
||||
{"createPlayer", nullptr, createPlayer, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"prepare", nullptr, prepare, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"play", nullptr, play, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"pause", nullptr, pause, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"stop", nullptr, stop, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"release", nullptr, release, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"attachSurface", nullptr, attachSurface, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"detachSurface", nullptr, detachSurface, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"onForeground", nullptr, onForeground, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"onBackground", nullptr, onBackground, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"onSurfaceAvailable", nullptr, onSurfaceAvailable, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"onSurfaceDestroyed", nullptr, onSurfaceDestroyed, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"seekTo", nullptr, seekTo, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"seekBy", nullptr, seekBy, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"setSpeed", nullptr, setSpeed, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"setVolume", nullptr, setVolume, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"setMute", nullptr, setMute, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"selectAudioTrack", nullptr, selectAudioTrack, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"selectSubtitleTrack", nullptr, selectSubtitleTrack, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"disableSubtitle", nullptr, disableSubtitle, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"addSubtitle", nullptr, addSubtitle, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"removeSubtitle", nullptr, removeSubtitle, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"setSubtitleDelay", nullptr, setSubtitleDelay, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"setSubtitleScale", nullptr, setSubtitleScale, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"setSubtitlePosition", nullptr, setSubtitlePosition, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"getState", nullptr, getState, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"getMediaInfo", nullptr, getMediaInfo, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"getTracks", nullptr, getTracks, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"getMetrics", nullptr, getMetrics, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"getBuildInfo", nullptr, getBuildInfo, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
};
|
||||
napi_define_properties(env, exports, sizeof(properties) / sizeof(properties[0]), properties);
|
||||
return exports;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
NAPI_MODULE(velix_core_binding, Init)
|
||||
Reference in new issue
Block a user