* Copyright (c) 2026 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 "napi/native_api.h"
#include "AudioVividWrapper.h"
#include "AudioVividPlaybackManager.h"
#include "AudioVividFlvMuxerManager.h"
#include "AudioConfig.h"
#include "AudioLog.h"
#include <memory>
#include <cstring>
static std::unique_ptr<AudioVividWrapper> g_audioVividWrapper = nullptr;
static std::unique_ptr<AudioVividPlaybackManager> g_playbackManager = nullptr;
static std::unique_ptr<AudioVividFlvMuxerManager> g_flvMuxerManager = nullptr;
static napi_value InitializeVivid(napi_env env, napi_callback_info info)
{
g_audioVividWrapper = std::make_unique<AudioVividWrapper>();
bool success = g_audioVividWrapper->Initialize();
napi_value result;
napi_get_boolean(env, success, &result);
return result;
}
static napi_value UpdateObjectPosition(napi_env env, napi_callback_info info)
{
size_t argc = 4;
napi_value args[4] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
if (!g_audioVividWrapper) {
napi_throw_error(env, nullptr, "AudioVivid not initialized");
return nullptr;
}
int32_t objectIndex;
double x;
double y;
double z;
napi_get_value_int32(env, args[0], &objectIndex);
napi_get_value_double(env, args[1], &x);
napi_get_value_double(env, args[2], &y);
napi_get_value_double(env, args[3], &z);
bool success = g_audioVividWrapper->UpdateObjectPosition(
objectIndex, static_cast<float>(x), static_cast<float>(y), static_cast<float>(z));
napi_value result;
napi_get_boolean(env, success, &result);
return result;
}
static napi_value UpdateObjectGain(napi_env env, napi_callback_info info)
{
size_t argc = 2;
napi_value args[2] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
if (!g_audioVividWrapper) {
napi_throw_error(env, nullptr, "AudioVivid not initialized");
return nullptr;
}
int32_t objectIndex = 0;
double gain = 0.0;
napi_get_value_int32(env, args[0], &objectIndex);
napi_get_value_double(env, args[1], &gain);
AVCODEC_SAMPLE_LOGI("UpdateObjectGain in C++: objectIndex=%d, gain(double)=%f, gain(float)=%f",
objectIndex,
gain,
static_cast<float>(gain));
bool success = g_audioVividWrapper->UpdateObjectGain(objectIndex, static_cast<float>(gain));
napi_value result;
napi_get_boolean(env, success, &result);
return result;
}
static napi_value GetMetadata(napi_env env, napi_callback_info info)
{
size_t argc = 1;
napi_value args[1] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
if (!g_audioVividWrapper) {
napi_throw_error(env, nullptr, "AudioVivid not initialized");
return nullptr;
}
bool withStaticMeta = false;
if (argc >= 1) {
napi_get_value_bool(env, args[0], &withStaticMeta);
}
std::vector<uint8_t> metadata = g_audioVividWrapper->GetMetadata(withStaticMeta);
void *data = nullptr;
napi_value arrayBuffer;
napi_create_arraybuffer(env, metadata.size(), &data, &arrayBuffer);
memcpy(data, metadata.data(), metadata.size());
return arrayBuffer;
}
static napi_value StartPlayback(napi_env env, napi_callback_info info)
{
g_playbackManager = std::make_unique<AudioVividPlaybackManager>();
int32_t ret = g_playbackManager->Initialize();
if (ret != 0) {
napi_throw_error(env, nullptr, "Failed to initialize playback manager");
return nullptr;
}
ret = g_playbackManager->Start();
if (ret != 0) {
napi_throw_error(env, nullptr, "Failed to start playback");
return nullptr;
}
napi_value result;
napi_get_boolean(env, true, &result);
return result;
}
static napi_value StopPlayback(napi_env env, napi_callback_info info)
{
if (!g_playbackManager) {
napi_throw_error(env, nullptr, "Playback manager not initialized");
return nullptr;
}
int32_t ret = g_playbackManager->Stop();
if (ret != 0) {
napi_throw_error(env, nullptr, "Failed to stop playback");
return nullptr;
}
napi_value result;
napi_get_boolean(env, true, &result);
return result;
}
static napi_value UpdatePlaybackMetadata(napi_env env, napi_callback_info info)
{
size_t argc = 1;
napi_value args[1] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
if (argc < 1) {
napi_throw_error(env, nullptr, "Missing metadata argument");
return nullptr;
}
bool isArrayBuffer;
napi_is_arraybuffer(env, args[0], &isArrayBuffer);
if (!isArrayBuffer) {
napi_throw_error(env, nullptr, "Metadata must be an ArrayBuffer");
return nullptr;
}
void *data = nullptr;
size_t length = 0;
napi_get_arraybuffer_info(env, args[0], &data, &length);
if (!g_playbackManager) {
napi_throw_error(env, nullptr, "Playback manager not initialized");
return nullptr;
}
g_playbackManager->UpdateMetadata(static_cast<uint8_t *>(data), static_cast<int32_t>(length));
napi_value result;
napi_get_boolean(env, true, &result);
return result;
}
static napi_value UpdateFlvMuxMetadata(napi_env env, napi_callback_info info)
{
size_t argc = 1;
napi_value args[1] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
if (argc < 1) {
napi_throw_error(env, nullptr, "Missing metadata argument");
return nullptr;
}
bool isArrayBuffer;
napi_is_arraybuffer(env, args[0], &isArrayBuffer);
if (!isArrayBuffer) {
napi_throw_error(env, nullptr, "Metadata must be an ArrayBuffer");
return nullptr;
}
void *data = nullptr;
size_t length = 0;
napi_get_arraybuffer_info(env, args[0], &data, &length);
if (!g_flvMuxerManager) {
napi_throw_error(env, nullptr, "FLV muxer not initialized");
return nullptr;
}
g_flvMuxerManager->UpdateMetadata(static_cast<uint8_t *>(data), static_cast<int32_t>(length));
napi_value result;
napi_get_boolean(env, true, &result);
return result;
}
static napi_value StartFlvMux(napi_env env, napi_callback_info info)
{
size_t argc = 1;
napi_value args[1] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
int32_t fd = -1;
if (argc >= 1) {
napi_get_value_int32(env, args[0], &fd);
}
if (fd < 0) {
napi_throw_error(env, nullptr, "Invalid fd");
return nullptr;
}
AVCODEC_SAMPLE_LOGI("StartFlvMux: fd = %{public}d", fd);
g_flvMuxerManager = std::make_unique<AudioVividFlvMuxerManager>();
int32_t ret = g_flvMuxerManager->Initialize(fd);
if (ret != 0) {
const char *steps[] = {"", "PCM文件打开失败", "编码器创建失败", "编码器配置失败",
"OH_AVMuxer创建失败", "音频格式创建失败", "添加音频轨失败", "封装器启动失败"};
std::string errMsg = "初始化失败[步骤" + std::to_string(ret) + "]: ";
errMsg += (ret >= 1 && ret <= 7) ? steps[ret] : "未知错误";
napi_throw_error(env, nullptr, errMsg.c_str());
return nullptr;
}
ret = g_flvMuxerManager->Start();
if (ret != 0) {
napi_throw_error(env, nullptr, "Failed to start FLV muxing");
return nullptr;
}
napi_value result;
napi_get_boolean(env, true, &result);
return result;
}
static napi_value StopFlvMux(napi_env env, napi_callback_info info)
{
if (!g_flvMuxerManager) {
napi_throw_error(env, nullptr, "FLV muxer not initialized");
return nullptr;
}
int32_t ret = g_flvMuxerManager->Stop();
if (ret != 0) {
napi_throw_error(env, nullptr, "Failed to stop FLV muxing");
return nullptr;
}
napi_value result;
napi_get_boolean(env, true, &result);
return result;
}
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
napi_property_descriptor desc[] = {
{"initializeVivid", nullptr, InitializeVivid, nullptr, nullptr, nullptr, napi_default, nullptr},
{"updateObjectPosition", nullptr, UpdateObjectPosition, nullptr, nullptr, nullptr, napi_default, nullptr},
{"updateObjectGain", nullptr, UpdateObjectGain, nullptr, nullptr, nullptr, napi_default, nullptr},
{"getMetadata", nullptr, GetMetadata, nullptr, nullptr, nullptr, napi_default, nullptr},
{"startPlayback", nullptr, StartPlayback, nullptr, nullptr, nullptr, napi_default, nullptr},
{"stopPlayback", nullptr, StopPlayback, nullptr, nullptr, nullptr, napi_default, nullptr},
{"updatePlaybackMetadata", nullptr, UpdatePlaybackMetadata, nullptr, nullptr, nullptr, napi_default, nullptr},
{"startFlvMux", nullptr, StartFlvMux, nullptr, nullptr, nullptr, napi_default, nullptr},
{"stopFlvMux", nullptr, StopFlvMux, nullptr, nullptr, nullptr, napi_default, nullptr},
{"updateFlvMuxMetadata", nullptr, UpdateFlvMuxMetadata, nullptr, nullptr, nullptr, napi_default, nullptr},
};
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
return exports;
}
EXTERN_C_END
static napi_value Add(napi_env env, napi_callback_info info)
{
size_t argc = 2;
napi_value args[2] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
napi_valuetype valuetype0;
napi_typeof(env, args[0], &valuetype0);
napi_valuetype valuetype1;
napi_typeof(env, args[1], &valuetype1);
double value0;
napi_get_value_double(env, args[0], &value0);
double value1;
napi_get_value_double(env, args[1], &value1);
napi_value sum;
napi_create_double(env, value0 + value1, &sum);
return sum;
}
static napi_module demoModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Init,
.nm_modname = "entry",
.nm_priv = ((void *)0),
.reserved = {0},
};
extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
{
napi_module_register(&demoModule);
}