Skip to content

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

增加支持输出到指定的句柄 id 上,并且支持暂停和继续播放

为了支持输出到指定的句柄 ID(例如,一个窗口句柄)以及支持暂停和继续播放功能,你需要进行以下修改:

  1. 输出到指定句柄:使用 libvlc_media_player_set_hwnd() 函数来设置播放输出的句柄。

  2. 暂停和继续播放:使用 libvlc_media_player_pause() 函数来暂停或继续播放。如果播放器处于播放状态,该函数将暂停它;如果处于暂停状态,它将恢复播放。

下面是 C++代码的修改部分:

cpp
libvlc_media_player_t *mp = nullptr; // Make it global to control it from other functions.

void PlayMedia(const FunctionCallbackInfo<Value>& args) {
    // ... (The beginning remains the same)

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

    if (args.Length() > 2 && args[2]->IsNumber()) {
        uintptr_t hwnd = args[2]->IntegerValue(isolate->GetCurrentContext()).FromJust();
        libvlc_media_player_set_hwnd(mp, (void*)hwnd);
    }

    libvlc_media_player_play(mp);
}

void PauseOrResume(const FunctionCallbackInfo<Value>& args) {
    if (mp) {
        libvlc_media_player_pause(mp);
    }
}

然后,你需要在 Init 函数里注册新的 PauseOrResume 函数:

cpp
void Init(Local<Object> exports) {
    // ... (Other initialization code)

    NODE_SET_METHOD(exports, "playMedia", PlayMedia);
    NODE_SET_METHOD(exports, "pauseOrResume", PauseOrResume);
}

在 JavaScript 端,你的 API 使用方法如下:

javascript
const vlcAddon = require("./your_module_path");

// Play a local or remote media on a given window handle
vlcAddon.playMedia("path_or_url", 1000, windowHandle);

// Pause or resume the current media
vlcAddon.pauseOrResume();

其中,windowHandle 是你想在上面显示视频的窗口的句柄 ID。这通常是一个整数值,但其确切类型和格式取决于你的应用程序和平台。

这些修改应该提供了你需要的功能,但请记住在真实应用中进行充分的测试,确保一切正常工作。

addon.cc 修改内容如下

addon.cc 顶部增加 libvlc_media_player_t *mp

cpp
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;

libvlc_media_player_t *mp = nullptr; // Make it global to control it from other functions.

修改后的 PlayMedia 和新增的 PauseOrResume

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_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);

    if (args.Length() > 2 && args[2]->IsNumber()) {
        uintptr_t hwnd = args[2]->IntegerValue(isolate->GetCurrentContext()).FromJust();
        libvlc_media_player_set_hwnd(mp, (void*)hwnd);
    }

    libvlc_media_player_play(mp);
}

void PauseOrResume(const FunctionCallbackInfo<Value>& args) {
    Isolate* isolate = args.GetIsolate();
    bool isPlaying = false;

    if (mp) {
        if (libvlc_media_player_is_playing(mp)) {
            libvlc_media_player_pause(mp);
        } else {
            libvlc_media_player_play(mp);
            isPlaying = true;
        }
        fprintf(log_file, "PauseOrResume has mp: %p\n", mp);
        fflush(log_file);
    } else {
        fprintf(log_file, "PauseOrResume not mp: %p\n", mp);
        fflush(log_file);
    }

    // Return the playing status
    args.GetReturnValue().Set(Boolean::New(isolate, isPlaying));
}
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, "pauseOrResume", PauseOrResume);
    NODE_SET_METHOD(exports, "setLoggingEnabled", SetLoggingEnabled);
    NODE_SET_METHOD(exports, "setMaxLogSize", SetMaxLogSize);
}

这里增加日志写入“PauseOrResume has mp” 可以在 vlc_log.txt 查看调用暂停和开始的内容

index.js 修改增加 pauseOrResume 方法

js
module.exports = {
  /**
   * 播放给定URL的HTTP流。
   * @param {string} url - 要播放的流的URL。
   * @param {number} [cache=1000] - 缓存大小,单位为毫秒。默认为1000ms。
   */
  playMedia: addon.playMedia,
  /**
   * 暂停或继续当前的媒体播放。
   */
  pauseOrResume: addon.pauseOrResume,
};

main.js 中测试使用可用

js
vlcAddon.playMedia(newURL, 5000); // 5000ms caching

setTimeout(() => {
  vlcAddon.pauseOrResume();
  console.log("调用暂停");

  setTimeout(() => {
    console.log("调用继续");
    vlcAddon.pauseOrResume();
  }, 5000);
}, 5000);

printf("PauseOrResume has mp: %p", mp); 是否可以输出到 log_filename 中,怎么修改

如果你想将 printf 的输出写入到 log_filename(即你的日志文件)中,你应该使用 fprintf 函数,它允许你指定一个文件指针作为输出目标。

更改这一行:

cpp
printf("PauseOrResume has mp: %p", mp);

为:

cpp
fprintf(log_file, "PauseOrResume has mp: %p\n", mp);

和这一行:

cpp
printf("PauseOrResume not mp: %p", mp);

为:

cpp
fprintf(log_file, "PauseOrResume not mp: %p\n", mp);

这样,输出就会写入到你的日志文件中。

另外,确保在写入后,你调用 fflush(log_file);,以确保日志消息立即被写入文件:

cpp
fprintf(log_file, "PauseOrResume has mp: %p\n", mp);
fflush(log_file);

和:

cpp
fprintf(log_file, "PauseOrResume not mp: %p\n", mp);
fflush(log_file);

这样,即使程序在之后崩溃或被中断,你仍然可以看到这些日志消息。