Skip to content

Node 使用 VLC 播放音视频开发文档 - 1

开发环境

参考 electron dll 开发环境 https://docs.ffffee.com/frontend/230401-install-vs2022.html

下载 VLC Windows SDK

前往 VLC 官方下载页面:https://get.videolan.org/vlc/

Windows 64 版本

找到 3.0.19 - win64 - vlc-3.0.19-win64.7z 解压出来

可以点击这个地址直接下载 Windows 64 的https://get.videolan.org/vlc/3.0.19/win64/vlc-3.0.19-win64.7z

Mac universal(苹果和英特尔芯片都支持) 版本

找到 3.0.19 - macosx - vlc-3.0.19-universal.dmg

直接双击 - 安装 - 安装之后在 你的 Application 中会有 VLC.app, 找到把 里面的 sdk 拷贝出来

可以点击这个链接下载 https://get.videolan.org/vlc/3.0.19/macosx/vlc-3.0.19-universal.dmg

1. 创建 pacakge.json

创建项目目录

bash
mkdir -p your-node-vlc-project
bash
cd your-node-vlc-project

创建 index.js 、main.js 文件

bash
# windows 使用 ni index.js 、ni main.js
touch index.js main.js

创建 package.json 文件

bash
npm init -y

2. 安装相关模块

bash
# 注意这里要指定版本是因为get-port新版本要支持es6方式引入
npm install http-proxy get-port@5.1.1 -S
bash
npm install nodemon

修改 package.json scripts

json
  "scripts": {
    "start": "nodemon main.js",
    "rebuild": "node-gyp rebuild"
  },

此时 package.json 应该这样子

json
{
  "name": "your-node-vlc-project",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "dependencies": {
    "get-port": "^5.1.1",
    "http-proxy": "^1.18.1"
  },
  "devDependencies": {
    "nodemon": "^3.0.1"
  },
  "scripts": {
    "start": "nodemon main.js",
    "rebuild": "node-gyp rebuild"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "gypfile": true
}

3. 编写 Windows c++ 代码

创建 addon.cc

bash
# Windows 使用 ni addon.cc
touch addon.cc

addon.cc 代码如下

c
#include <iostream>
#include <node.h>
#include <vlc/vlc.h>

using namespace v8;

void PlayHTTPStream(const FunctionCallbackInfo<Value>& args) {
    Isolate* isolate = args.GetIsolate();

    if (args.Length() < 1 || !args[0]->IsString()) {
        isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Expected a string argument for the URL").ToLocalChecked()));
        return;
    }

    String::Utf8Value url(isolate, args[0]);
    int cache = args[1]->IsNumber() ? args[1]->Int32Value(isolate->GetCurrentContext()).FromJust() : 1000; // Default 1000ms

    libvlc_instance_t *inst;
    libvlc_media_player_t *mp;
    libvlc_media_t *m;

    inst = libvlc_new(0, nullptr);
    m = libvlc_media_new_location(inst, *url);

    std::string cache_option = ":network-caching=" + std::to_string(cache);
    libvlc_media_add_option(m, cache_option.c_str());
    // libvlc_media_add_option(m, ":avcodec-codec=libavcodec");
    // libvlc_media_add_option(m, ":vout=opengl");
    // libvlc_media_add_option(m, ":gl-sw");
    // libvlc_media_add_option(m, ":avcodec-hw=any"); // Enable hardware acceleration
    // libvlc_media_add_option(m, ":avcodec-hw=none"); // Disable hardware acceleration

    mp = libvlc_media_player_new_from_media(m);
    libvlc_media_release(m);

    libvlc_media_player_play(mp);
}

void Init(Local<Object> exports) {
    NODE_SET_METHOD(exports, "playHTTPStream", PlayHTTPStream);
}

NODE_MODULE(addon, Init)

创建 binding.gyp 文件

bash
touch binding.gyp

binding.gyp 文件内容如下

json
{
  "targets": [
    {
      "target_name": "vlc_node_extension",
      "sources": ["addon.cc"],
      "conditions": [
        [
          "OS=='win'",
          {
            "include_dirs": [
              "C:\\Users\\Administrator\\Desktop\\electron-vlc\\vlc-3.0.19-win64\\vlc-3.0.19\\sdk\\include"
            ],
            "libraries": [
              "C:\\Users\\Administrator\\Desktop\\electron-vlc\\vlc-3.0.19-win64\\vlc-3.0.19\\sdk\\lib\\libvlc.lib",
              "C:\\Users\\Administrator\\Desktop\\electron-vlc\\vlc-3.0.19-win64\\vlc-3.0.19\\sdk\\lib\\libvlccore.lib"
            ]
          }
        ],
        [
          "OS!='win'",
          {
            "include_dirs": [
              "/Users/shaohai.li/project/electron-vlc/vlc-3.0.18-universal/include"
            ],
            "libraries": [
              "/Users/shaohai.li/project/electron-vlc/vlc-3.0.18-universal/lib/libvlc.dylib",
              "/Users/shaohai.li/project/electron-vlc/vlc-3.0.18-universal/lib/libvlccore.dylib"
            ],
            "link_settings": {
              "libraries": [
                "-Wl,-rpath,@loader_path/../../vlc-3.0.18-universal/lib"
              ]
            }
          }
        ]
      ]
    }
  ]
}
  • 注意这里要改成你自己的 C:\\Users\\Administrator\\Desktop\\electron-vlc\\vlc-3.0.19-win64\\vlc-3.0.19

  • vlc-3.0.19 请看开头的 开发环境 - 下载 VLC Windows SDK 章节

4. 使用 node-gyp 编译的 addon.cc 代码

清理当前的编译目录 - 会删掉当前目录下的 build 目录(如果有)

bash
node-gyp clean

生成相关配置 会调用 binding.gyp

bash
node-gyp configure

使用 node-gyp 构建生成.node 文件

bash
node-gyp build

会依次执行 上面的三个命令,clean、configure、build

这里注意 修改源码后必须 rebuild 当然你也可以依次执行这三个(clean、configure、build)命令

bash
node-gyp rebuild

使用 npm 的 scripts 脚本进行 rebuild

bash
npm run rebuild

下面是这几个命令的运行日志,如果和我一样就说明你的环境配置没有问题,遇到问了在解决对应的问题

bash
PS C:\Users\Administrator\Desktop\node-vlc> node-gyp clean
gyp info it worked if it ends with ok
gyp info using node-gyp@9.4.0
gyp info using node@18.17.0 | win32 | x64
gyp info ok
PS C:\Users\Administrator\Desktop\node-vlc> node-gyp configure
gyp info it worked if it ends with ok
gyp info using node-gyp@9.4.0
gyp info using node@18.17.0 | win32 | x64
gyp info find Python using Python version 3.9.12 found at "D:\tools\python3\python.exe"
gyp info find VS using VS2022 (17.6.33829.357) found at:
gyp info find VS run with --verbose for detailed information
gyp info spawn D:\tools\python3\python.exe
gyp info spawn args [
gyp info spawn args   'C:\\Users\\Administrator\\AppData\\Roaming\\npm\\node_modules\\node-gyp\\gyp\\gyp_main.py',
gyp info spawn args   'binding.gyp',
gyp info spawn args   '-f',
gyp info spawn args   'msvs',
gyp info spawn args   '-I',
gyp info spawn args   'C:\\Users\\Administrator\\Desktop\\node-vlc\\build\\config.gypi',
gyp info spawn args   '-I',
gyp info spawn args   'C:\\Users\\Administrator\\AppData\\Roaming\\npm\\node_modules\\node-gyp\\addon.gypi',
gyp info spawn args   '-I',
gyp info spawn args   'C:\\Users\\Administrator\\AppData\\Local\\node-gyp\\Cache\\18.17.0\\include\\node\\common.gypi',
gyp info spawn args   '-Dlibrary=shared_library',
gyp info spawn args   '-Dvisibility=default',
gyp info spawn args   '-Dnode_root_dir=C:\\Users\\Administrator\\AppData\\Local\\node-gyp\\Cache\\18.17.0',
gyp info spawn args   '-Dnode_gyp_dir=C:\\Users\\Administrator\\AppData\\Roaming\\npm\\node_modules\\node-gyp',
gyp info spawn args   '-Dnode_lib_file=C:\\\\Users\\\\Administrator\\\\AppData\\\\Local\\\\node-gyp\\\\Cache\\\\18.17.0\\\\<(target_arch)\\\\node.lib',
gyp info spawn args   '-Dmodule_root_dir=C:\\Users\\Administrator\\Desktop\\node-vlc',
gyp info spawn args   '--depth=.',
gyp info spawn args   '--no-parallel',
gyp info spawn args   '--generator-output',
gyp info spawn args   'C:\\Users\\Administrator\\Desktop\\node-vlc\\build',
gyp info spawn args   '-Goutput_dir=.'
gyp info spawn args ]
gyp info ok
PS C:\Users\Administrator\Desktop\node-vlc> node-gyp build
gyp info it worked if it ends with ok
gyp info using node-gyp@9.4.0
gyp info using node@18.17.0 | win32 | x64
gyp info spawn C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exe
gyp info spawn args [
gyp info spawn args   'build/binding.sln',
gyp info spawn args   '/clp:Verbosity=minimal',
gyp info spawn args   '/nologo',
gyp info spawn args   '/p:Configuration=Release;Platform=x64'
gyp info spawn args ]
在此解决方案中一次生成一个项目。若要启用并行生成,请添加“-m”开关。

  addon.cc
  win_delay_load_hook.cc
  正在生成代码
  Previous IPDB not found, fall back to full compilation.
  All 139 functions were compiled because no usable IPDB/IOBJ from previous compilation was found.
  已完成代码的生成
  vlc_node_extension.vcxproj -> C:\Users\Administrator\Desktop\node-vlc\build\Release\\vlc_node_extension.node
gyp info ok
PS C:\Users\Administrator\Desktop\node-vlc> node-gyp rebuild
gyp info it worked if it ends with ok
gyp info using node-gyp@9.4.0
gyp info using node@18.17.0 | win32 | x64
gyp info find Python using Python version 3.9.12 found at "D:\tools\python3\python.exe"
gyp info find VS using VS2022 (17.6.33829.357) found at:
gyp info find VS "C:\Program Files\Microsoft Visual Studio\2022\Community"
gyp info find VS run with --verbose for detailed information
gyp info spawn D:\tools\python3\python.exe
gyp info spawn args [
gyp info spawn args   'C:\\Users\\Administrator\\AppData\\Roaming\\npm\\node_modules\\node-gyp\\gyp\\gyp_main.py',
gyp info spawn args   'binding.gyp',
gyp info spawn args   '-f',
gyp info spawn args   'msvs',
gyp info spawn args   '-I',
gyp info spawn args   'C:\\Users\\Administrator\\Desktop\\node-vlc\\build\\config.gypi',
gyp info spawn args   '-I',
gyp info spawn args   'C:\\Users\\Administrator\\AppData\\Roaming\\npm\\node_modules\\node-gyp\\addon.gypi',
gyp info spawn args   '-I',
gyp info spawn args   'C:\\Users\\Administrator\\AppData\\Local\\node-gyp\\Cache\\18.17.0\\include\\node\\common.gypi',
gyp info spawn args   '-Dlibrary=shared_library',
gyp info spawn args   '-Dvisibility=default',
gyp info spawn args   '-Dnode_root_dir=C:\\Users\\Administrator\\AppData\\Local\\node-gyp\\Cache\\18.17.0',
gyp info spawn args   '-Dnode_gyp_dir=C:\\Users\\Administrator\\AppData\\Roaming\\npm\\node_modules\\node-gyp',
gyp info spawn args   '-Dnode_lib_file=C:\\\\Users\\\\Administrator\\\\AppData\\\\Local\\\\node-gyp\\\\Cache\\\\18.17.0\\\\<(target_arch)\\\\node.lib',
gyp info spawn args   '-Dmodule_root_dir=C:\\Users\\Administrator\\Desktop\\node-vlc',
gyp info spawn args   '-Dnode_engine=v8',
gyp info spawn args   '--depth=.',
gyp info spawn args   '--no-parallel',
gyp info spawn args   '--generator-output',
gyp info spawn args   'C:\\Users\\Administrator\\Desktop\\node-vlc\\build',
gyp info spawn args   '-Goutput_dir=.'
gyp info spawn args ]
gyp info spawn C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exe
gyp info spawn args [
gyp info spawn args   'build/binding.sln',
gyp info spawn args   '/clp:Verbosity=minimal',
gyp info spawn args   '/nologo',
gyp info spawn args   '/p:Configuration=Release;Platform=x64'
gyp info spawn args ]
在此解决方案中一次生成一个项目。若要启用并行生成,请添加“-m”开关。

  addon.cc
  win_delay_load_hook.cc
  正在生成代码
  Previous IPDB not found, fall back to full compilation.
  All 139 functions were compiled because no usable IPDB/IOBJ from previous compilation was found.
  已完成代码的生成
  vlc_node_extension.vcxproj -> C:\Users\Administrator\Desktop\node-vlc\build\Release\\vlc_node_extension.node
gyp info ok
PS C:\Users\Administrator\Desktop\node-vlc>

5. 编写 index.js 代码

当你 build、或者 rebuild 之后会自动生成该文件 ./build/Release/vlc_node_extension.node

这个就是你的.node c++插件,我们可以通过下面的代码调用

index.js 内容如下

js
const addon = require("./build/Release/vlc_node_extension.node");

console.log("addon", addon);

在你的终端 powershell 中执行 node 命令

bash
node index.js

会出现这个错误

bash
PS C:\Users\Administrator\Desktop\node-vlc> node .\index.js
node:internal/modules/cjs/loader:1340
  return process.dlopen(module, path.toNamespacedPath(filename));
                 ^

Error: The specified module could not be found.
\\?\C:\Users\Administrator\Desktop\node-vlc\build\Release\vlc_node_extension.node
    at Module._extensions..node (node:internal/modules/cjs/loader:1340:18)
    at Module.load (node:internal/modules/cjs/loader:1119:32)
    at Module._load (node:internal/modules/cjs/loader:960:12)
    at Module.require (node:internal/modules/cjs/loader:1143:19)
    at require (node:internal/modules/cjs/helpers:110:18)
    at Object.<anonymous> (C:\Users\Administrator\Desktop\node-vlc\index.js:1:15)
    at Module._compile (node:internal/modules/cjs/loader:1256:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1310:10)
    at Module.load (node:internal/modules/cjs/loader:1119:32)
    at Module._load (node:internal/modules/cjs/loader:960:12) {
  code: 'ERR_DLOPEN_FAILED'
}

Node.js v18.17.0
PS C:\Users\Administrator\Desktop\node-vlc>

这个错误是什么原因导致的呢?

是因为我们 vlc_node_extension.node 文件找不到 对应的依赖的 libvlc.dll 等动态库

就会出现 Error: The specified module could not be found.

这个时候需要我们了解 node.js 中.node 文件的 c++ 插件 寻找动态库的逻辑

首先会在可以执行的文件相同的目录去找,如果没有找到, 那么会在系统的环境变量中找,

把 dll 放到当前.node 目录下不太现实,因为.node 是动态生成的, 那么此时需要我们把 libvlc 的安装目录放到系统的环境变量中就可以

如何修改系统环境变量(Windows 10) - 可以通过 此电脑 - 属性 - 高级系统设置 - 高级 - 当前用户的环境变量或者系统环境变量都可以 - PATH中添加我们一开始就下载 Windows SDK解压后的目录

但是我们要介绍一个更简单的方法,不需要手动设置环境变量,因为我们的应用是要放在安装包中,不能让所有用都自己安装 vlc sdk 以及去设置环境变量

这里就引出了 node.js 的 process.env.PATH, 你打印一下这个值,会发现 process.env.PATH 就是你配置的系统环境变量的字符串

我们的应用依赖的就是这个路径,如果我们手动修改这个值,那么就可以让 应用打开的时候按照我们的给他的路径去查找了

综上,我们最终的方案是把我们的安装包目录放到程序中,修改 process.env.PATH的值来实现 修复上面找不到 libvlc dll 的问题

更新后的 index.js 代码如下

js
const dllPath =
  "C:\\Users\\Administrator\\Desktop\\electron-vlc\\vlc-3.0.19-win64\\vlc-3.0.19";
process.env.PATH = dllPath + ";" + process.env.PATH;

const addon = require("./build/Release/vlc_node_extension.node");

console.log("addon", addon);
  • dllPath 就是你的 vlc 安装目录 - 也就是解压后的目录 - 这里改成你自己的

再次运行 node index.js

bash
PS C:\Users\Administrator\Desktop\node-vlc> node .\index.js
addon { playHTTPStream: [Function: playHTTPStream] }
PS C:\Users\Administrator\Desktop\node-vlc>

如果你和我一样的输出说明就是成功了

6. 调用 libvlc 的 c++插件 打开一个 http 的视频

创建一个 express 托管的静态服务

bash
mkdir -p vlc-video-server
bash
mkdir -p vlc-video-server/videos
bash
touch server.js
js
const express = require("express");
const app = express();
const port = 3000;
app.use("/", express.static("videos"));
app.listen(port, () => {
  console.log(`Server is running at http://localhost:${port}`);
});

把你的视频文件放到 vlc-video-server/videos 目录中

然后通过

bash
npm init -y
bash
npm i express

启动你的 node.js 服务

bash
node server.js

通过浏览器访问 http://localhost:3000/your-video.mp4

应该可以看到你的视频

修改 main.js

如果没有 main.js 文件,那么创建一个

bash
touch main.js

main.js 内容如下

js
const vlcAddon = require("./index.js");
const ProxyServer = require("./proxyServer.js");
main();

async function main() {
  const proxy = new ProxyServer();

  await proxy.start();

  const params = {
    fileUrl: "http://localhost:3000/water.mp4",
    token: "test",
  };

  params.token = "eyJ0eXAiOiJ";

  const myURL = new URL(params.fileUrl);
  myURL.searchParams.append("token", params.token);
  myURL.searchParams.append("fileUrl", myURL.origin);
  const localURL = `http://localhost:${proxy.port}`;
  const pathname = myURL.pathname;

  const searchParams = myURL.searchParams.toString();
  const newURL = `${localURL}${pathname}?${searchParams}`;

  console.log("newURL", newURL);

  vlcAddon.playHTTPStream(newURL, 5000); // 5000ms caching
}
  • fileUrl: "http://localhost:3000/water.mp4", 改成你自己的

这里请求的是本地的 localhost 代理,是因为 vlc 不支持配置请求头等自定义的内容

一般我们的服务器资源可能需要 Token 这些就没有办法使用 vlc 直接播放了

通过本地代理,可以把 token 放到 url 中,然后在代理的地方 把 token 取出来,加入到请求头中就可以解决 vlc 无法更改请求头的问题了

此时我们还缺少一个 proxyServer.js 文件

创建 proxyServer.js 文件

代码如下

js
const http = require("http");
const getPort = require("get-port");
const { URL } = require("url");
const httpProxy = require("http-proxy");
const https = require("https");
const proxy = httpProxy.createProxyServer({});
const httpsAgent = new https.Agent({ rejectUnauthorized: false });
proxy.on("proxyReq", (proxyReq, req, res, options) => {
  proxyReq.setHeader("Authorization", `Bearer ${options.token}`);
});
class ProxyServer {
  constructor() {
    this.server = null;
    this.port = null;
    this.proxy = null;
  }
  async start() {
    this.server = http.createServer(async (req, res) => {
      const vlcURL = new URL("http://localhost" + req.url);
      const fileUrl = vlcURL.searchParams.get("fileUrl");
      const token = vlcURL.searchParams.get("token");
      vlcURL.searchParams.delete("fileUrl");
      vlcURL.searchParams.delete("token");
      vlcURL.searchParams.delete("path");

      const headers = { ...req.headers };

      if (token) {
        headers.Authorization = `Bearer ${token}`;
      }

      delete headers["user-agent"];
      delete headers["host"];
      delete headers["accept-language"];
      delete headers["accept"];

      const isHttps = fileUrl.startsWith("https://");
      const agent = isHttps ? httpsAgent : null;

      proxy.web(req, res, {
        token,
        target: fileUrl,
        changeOrigin: true,
        agent,
      });
    });

    await this.startWithRetry(5);
  }

  async startWithRetry(retries) {
    let success = false;

    while (retries > 0) {
      this.port = await getPort();
      try {
        await this.attemptStart();
        success = true;
        break;
      } catch (e) {
        console.error(
          `Error starting server on port ${this.port}: ${e.message}`
        );
        retries--;
      }
    }

    if (!success) {
      throw new Error(
        "Failed to start the proxy server after multiple attempts."
      );
    }
  }

  attemptStart() {
    return new Promise((resolve, reject) => {
      this.server
        .listen(this.port, () => {
          console.log(`listening on port http://localhost:${this.port}`);
          resolve();
        })
        .on("error", (err) => {
          reject(err);
        });
    });
  }
  stop() {
    if (this.server) {
      this.server.close();
      this.server = null;
      this.port = null;
    }
  }
  getPort() {
    return this.port;
  }
}

module.exports = ProxyServer;

经过调试了上面代码支持 http 和 https,支持 自定义 token,你可以按照你的情况来修改

还有一点 index.js 没有导出 addon, 这里需要增加一行代码 module.exports = addon;

js
// index.js
const dllPath =
  "C:\\Users\\Administrator\\Desktop\\electron-vlc\\vlc-3.0.19-win64\\vlc-3.0.19";

process.env.PATH = dllPath + ";" + process.env.PATH;

const addon = require("./build/Release/vlc_node_extension.node");

console.log("addon", addon);

module.exports = addon;

7. 运行项目

看看你的 package.json 有没有这个,没有的话加上

json
  "scripts": {
    "start": "nodemon main.js",
    "rebuild": "node-gyp rebuild"
  },

此时你会打开一个窗口 并且播放你的 http/https 请求的视频了

bash
npm start

注意注意注意

遇到一个问题 - 就是当屏幕缩放比不是 100% 的时候,视频会模糊,

比如我设置分辨率是 4k(3840*2160)、缩放比为 200%的时候,播放一个1920*1080的视频 会模糊失真

当我把电脑的缩放比修改 100%的时候就清晰了,这个已知的问题后续解决

8. 增加一个 vlc 播放的日志文件,便于分析问题

修改后的 addon.cc 代码如下

c
#include <iostream>
#include <node.h>
#include <vlc/vlc.h>
#include <sys/stat.h>

using namespace v8;

FILE* log_file = nullptr;
const char* log_filename = "vlc_log.txt";
const char* old_log_filename = "vlc_log.old.txt";
const int default_max_log_size = 1 * 1024 * 1024; // 1MB
int max_log_size = default_max_log_size;
bool logging_enabled = true;

void check_log_size_and_rotate() {
    struct stat st;
    if (fstat(fileno(log_file), &st) == 0 && st.st_size > max_log_size) {
        fclose(log_file);
        remove(old_log_filename);
        rename(log_filename, old_log_filename);
        log_file = fopen(log_filename, "w");
    }
}

void log_callback(void* data, int level, const libvlc_log_t* ctx, const char* fmt, va_list args) {
    if (log_file && logging_enabled) {
        vfprintf(log_file, fmt, args);
        fprintf(log_file, "\n");
        fflush(log_file);
        check_log_size_and_rotate();
    }
}

void SetLoggingEnabled(const FunctionCallbackInfo<Value>& args) {
    Isolate* isolate = args.GetIsolate();
    if (args.Length() < 1 || !args[0]->IsBoolean()) {
        isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Expected a boolean argument for logging status").ToLocalChecked()));
        return;
    }
    logging_enabled = args[0]->BooleanValue(isolate);
}

void SetMaxLogSize(const FunctionCallbackInfo<Value>& args) {
    Isolate* isolate = args.GetIsolate();
    if (args.Length() < 1 || !args[0]->IsNumber()) {
        isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Expected a number argument for max log size").ToLocalChecked()));
        return;
    }
    max_log_size = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust();
}

void PlayHTTPStream(const FunctionCallbackInfo<Value>& args) {
    Isolate* isolate = args.GetIsolate();

    if (args.Length() < 1 || !args[0]->IsString()) {
        isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Expected a string argument for the URL").ToLocalChecked()));
        return;
    }

    String::Utf8Value url(isolate, args[0]);
    int cache = args[1]->IsNumber() ? args[1]->Int32Value(isolate->GetCurrentContext()).FromJust() : 1000; // Default 1000ms

    libvlc_instance_t *inst;
    libvlc_media_player_t *mp;
    libvlc_media_t *m;

    inst = libvlc_new(0, nullptr);
    libvlc_log_set(inst, log_callback, log_file); // Set log callback
    m = libvlc_media_new_location(inst, *url);

    std::string cache_option = ":network-caching=" + std::to_string(cache);
    libvlc_media_add_option(m, cache_option.c_str());

    mp = libvlc_media_player_new_from_media(m);
    libvlc_media_release(m);

    libvlc_media_player_play(mp);
}

void Init(Local<Object> exports) {
    log_file = fopen(log_filename, "a"); // Open or append to existing log file
    if (!log_file) {
        fprintf(stderr, "Failed to open %s for logging\n", log_filename);
        return;
    }
    NODE_SET_METHOD(exports, "playHTTPStream", PlayHTTPStream);
    NODE_SET_METHOD(exports, "setLoggingEnabled", SetLoggingEnabled);
    NODE_SET_METHOD(exports, "setMaxLogSize", SetMaxLogSize);
}

NODE_MODULE(addon, Init)

别忘记了 node-gyp rebuild 重新构建

bash
node-gyp rebuild

修改 index.js 测试是否可以用

js
const dllPath =
  "C:\\Users\\Administrator\\Desktop\\electron-vlc\\vlc-3.0.19-win64\\vlc-3.0.19";
process.env.PATH = dllPath + ";" + process.env.PATH;

const addon = require("./build/Release/vlc_node_extension.node");
console.log("addon", addon);

module.exports = {
  /**
   * 播放给定URL的HTTP流。
   * @param {string} url - 要播放的流的URL。
   * @param {number} [cache=1000] - 缓存大小,单位为毫秒。默认为1000ms。
   */
  playHTTPStream: addon.playHTTPStream,
  /**
   * 设置是否启用日志记录。
   * @param {boolean} enable - 如果为true,则启用日志记录;如果为false,则禁用。
   */
  setLoggingEnabled: addon.setLoggingEnabled,
  /**
   * 设置日志文件的最大大小。
   * @param {number} size - 日志文件的最大大小,以字节为单位。
   */
  setMaxLogSize: addon.setMaxLogSize,
};

// const vlcAddon = require('./path_to_above_script');
// // 启用日志
// vlcAddon.setLoggingEnabled(true);
// // 设置日志大小为5MB
// vlcAddon.setMaxLogSize(5 * 1024 * 1024);
// // 使用playHTTPStream方法
// vlcAddon.playHTTPStream("http://example.com/stream");