Skip to content

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

1. 增加播放本地视频的功能

如果你希望支持播放本地视频,你需要对你的 C++扩展进行一些修改来支持本地文件路径。libVLC 库已经支持本地文件播放,所以你只需要提供一个额外的函数或稍微修改你的现有函数来接受本地文件路径。

以下是一个简单的方法,将 PlayHTTPStream 方法更改为一个更通用的 PlayMedia 方法,该方法可以接受 HTTP 流 URL 或本地文件路径:

addon.cc

cpp
void PlayMedia(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 media path or URL").ToLocalChecked()));
        return;
    }

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

    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

    // Check if the provided media path is an HTTP URL or a local file
    if (strncmp(*mediaPath, "http://", 7) == 0 || strncmp(*mediaPath, "https://", 8) == 0) {
        m = libvlc_media_new_location(inst, *mediaPath);
        std::string cache_option = ":network-caching=" + std::to_string(cache);
        libvlc_media_add_option(m, cache_option.c_str());
    } else {
        m = libvlc_media_new_path(inst, *mediaPath);
    }

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

    libvlc_media_player_play(mp);
}

在这个改进版本中,我们首先检查提供的媒体路径是否为 HTTP URL。如果是,则将其视为网络流;如果不是,则将其视为本地文件路径。

然后,根据你的 JavaScript 代码,你也应该更新导出的函数名称,例如从 playHTTPStream 更改为 playMedia

这样,你就可以使用这个新的 playMedia 函数播放本地文件或 HTTP 流了。

当然,如果你希望保留对 HTTP 流的专门支持,并为本地视频添加一个单独的函数,你也可以这样做,只需为本地文件播放创建一个新的函数。

2. 具体 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 PlayMedia(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 media path or URL").ToLocalChecked()));
        return;
    }

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

    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

    // Check if the provided media path is an HTTP URL or a local file
    if (strncmp(*mediaPath, "http://", 7) == 0 || strncmp(*mediaPath, "https://", 8) == 0) {
        m = libvlc_media_new_location(inst, *mediaPath);
        std::string cache_option = ":network-caching=" + std::to_string(cache);
        libvlc_media_add_option(m, cache_option.c_str());
    } else {
        m = libvlc_media_new_path(inst, *mediaPath);
    }

    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, "playMedia", PlayMedia);
    NODE_SET_METHOD(exports, "setLoggingEnabled", SetLoggingEnabled);
    NODE_SET_METHOD(exports, "setMaxLogSize", SetMaxLogSize);
}

NODE_MODULE(addon, Init)

3. 更新 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。
   */
  playMedia: addon.playMedia,
  /**
   * 设置是否启用日志记录。
   * @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);
// // 使用playMedia方法
// vlcAddon.playMedia("http://example.com/stream");

更新 main.js 中的方法

修改使用本地文件、远程文件进行播放测试

js
const params = {
  // fileUrl: "F:\\HifiCloud\\709-2020-4K-60-10bit.mp4",
  fileUrl: "http://localhost:3000/11-收尾-返回值规范.mp4",
  // fileUrl: "http://localhost:3000/222.mp4",
  // fileUrl: "http://localhost:3000/333.mp4",
  // fileUrl: "http://localhost:3000/water.mp4",
  // fileUrl: "http://docs.ffffee.com/2023-09-24-23-43-26.mp4",
  token: "test",
};
// ...
vlcAddon.playMedia(newURL, 5000); // 5000ms caching