electron VLC Player Video 技术调研
支持 Windows
支持 Mac
如何获取 path_to_vlc_sdk
path_to_vlc_sdk
是指您机器上 VLC SDK(即 libVLC)的路径。如果您正在使用 Windows,您需要下载 VLC SDK 并将其解压到您的机器上。
以下是如何获取 VLC SDK:
1. 下载 VLC Windows SDK
前往 VLC 官方下载页面: https://get.videolan.org/vlc/
根据您的需求选择相应的 VLC 版本。例如,选择 3.0.9.2
(或更高的版本)。在版本页面下方,您会看到 Win32
和 Win64
的下载链接。根据您的应用程序(32 位或 64 位)选择相应的链接。
比如下载这个文件:https://get.videolan.org/vlc/3.0.9.2/win64/vlc-3.0.9.2-win64.7z
在相应的页面,找到“SDK 包”链接,点击它以下载 ZIP 文件。
2. 解压缩 VLC SDK
一旦下载完成,解压 ZIP 文件到适当的位置,例如 C:\vlc-sdk\
。
3. 更新 binding.gyp
文件
现在,您知道了 SDK 的实际路径,您可以在 binding.gyp
文件中相应地替换 path_to_vlc_sdk
。例如:
"include_dirs": [
"C:/vlc-sdk/include"
],
"libraries": [
"-LC:/vlc-sdk/lib",
"-lvlc"
]
请注意,您应该使用正斜杠 /
,即使是在 Windows 中,因为 GYP 脚本通常期望这样的路径分隔符。
现在,您应该能够正确链接到 VLC SDK 并构建您的 Node.js 扩展。
https://chat.openai.com/c/46ceba4e-1631-4321-bade-5a2fb03527cf
GetPlayerState
LibVLC 为播放器的状态定义了多个枚举值。这里是完整的可能的状态列表,以及它们的简短描述:
libvlc_NothingSpecial
: 无特定状态,播放器初始化时的默认状态。libvlc_Opening
: 正在打开媒体。libvlc_Buffering
: 正在缓冲。libvlc_Playing
: 媒体正在播放。libvlc_Paused
: 媒体已暂停。libvlc_Stopped
: 媒体已停止。libvlc_Ended
: 媒体播放完毕。libvlc_Error
: 播放出错。
你可以在上面给出的GetPlayerState
函数中,使用switch
语句来处理所有这些状态,为每个状态返回一个相应的字符串值。例如:
switch (state) {
case libvlc_NothingSpecial:
stateString = String::NewFromUtf8(isolate, "nothing-special", v8::NewStringType::kNormal).ToLocalChecked();
break;
case libvlc_Opening:
stateString = String::NewFromUtf8(isolate, "opening", v8::NewStringType::kNormal).ToLocalChecked();
break;
case libvlc_Buffering:
stateString = String::NewFromUtf8(isolate, "buffering", v8::NewStringType::kNormal).ToLocalChecked();
break;
case libvlc_Playing:
stateString = String::NewFromUtf8(isolate, "playing", v8::NewStringType::kNormal).ToLocalChecked();
break;
case libvlc_Paused:
stateString = String::NewFromUtf8(isolate, "paused", v8::NewStringType::kNormal).ToLocalChecked();
break;
case libvlc_Stopped:
stateString = String::NewFromUtf8(isolate, "stopped", v8::NewStringType::kNormal).ToLocalChecked();
break;
case libvlc_Ended:
stateString = String::NewFromUtf8(isolate, "ended", v8::NewStringType::kNormal).ToLocalChecked();
break;
case libvlc_Error:
stateString = String::NewFromUtf8(isolate, "error", v8::NewStringType::kNormal).ToLocalChecked();
break;
default:
stateString = String::NewFromUtf8(isolate, "unknown", v8::NewStringType::kNormal).ToLocalChecked();
}
获取和设置 当前的播放速度
你可以使用以下两个 LibVLC 函数来获取和设置当前的播放速度:
libvlc_media_player_get_rate
:获取当前的播放速度。libvlc_media_player_set_rate
:设置播放速度。
以下是如何在main.cc
中将这两个功能暴露为 Node.js 的扩展方法:
void GetPlaybackRate(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
if (!mp) {
isolate->ThrowException(v8::Exception::Error(
String::NewFromUtf8(isolate, "Media player not initialized.", v8::NewStringType::kNormal).ToLocalChecked()
));
return;
}
float rate = libvlc_media_player_get_rate(mp);
args.GetReturnValue().Set(v8::Number::New(isolate, static_cast<double>(rate)));
}
void SetPlaybackRate(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
if (args.Length() < 1 || !args[0]->IsNumber()) {
isolate->ThrowException(v8::Exception::TypeError(
String::NewFromUtf8(isolate, "Expected playback rate as a number.", v8::NewStringType::kNormal).ToLocalChecked()
));
return;
}
if (!mp) {
isolate->ThrowException(v8::Exception::Error(
String::NewFromUtf8(isolate, "Media player not initialized.", v8::NewStringType::kNormal).ToLocalChecked()
));
return;
}
float rate = static_cast<float>(args[0]->NumberValue(isolate->GetCurrentContext()).ToChecked());
libvlc_media_player_set_rate(mp, rate);
}
// ... 在 Initialize 函数内部
NODE_SET_METHOD(exports, "getPlaybackRate", GetPlaybackRate);
NODE_SET_METHOD(exports, "setPlaybackRate", SetPlaybackRate);
这样,你就可以在 Electron 应用的 JavaScript 端调用这些方法来获取和设置当前的播放速度了。例如:
const rate = vlcAddon.getPlaybackRate();
vlcAddon.setPlaybackRate(1.5); // 设置为1.5倍速
注意:设置的播放速度应该在合理的范围内,太快或太慢可能会导致播放效果不佳。
PlayMedia 介绍
void PlayMedia(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
// Verify if the first parameter is a string (representing media path or URL)
if (args.Length() < 1 || !args[0]->IsString()) {
isolate->ThrowException(v8::Exception::TypeError(
String::NewFromUtf8(isolate, "Expected a media path string.", v8::NewStringType::kNormal).ToLocalChecked()
));
return;
}
v8::String::Utf8Value utf8Value(isolate, args[0]);
std::string mediaPath = std::string(*utf8Value);
libvlc_media_t* media;
if (mediaPath.substr(0, 7) == "http://" || mediaPath.substr(0, 8) == "https://") {
media = libvlc_media_new_location(inst, mediaPath.c_str());
// If the second parameter is provided and it is an object,
// then we assume it contains HTTP request header information
if (args.Length() > 1 && args[1]->IsObject()) {
Local<v8::Object> headersObj = args[1].As<v8::Object>();
Local<v8::Array> propertyNames = headersObj->GetOwnPropertyNames(isolate->GetCurrentContext()).ToLocalChecked();
for (uint32_t i = 0; i < propertyNames->Length(); i++) {
Local<Value> key = propertyNames->Get(isolate->GetCurrentContext(), i).ToLocalChecked();
Local<Value> value = headersObj->Get(isolate->GetCurrentContext(), key).ToLocalChecked();
v8::String::Utf8Value keyStr(isolate, key);
v8::String::Utf8Value valueStr(isolate, value);
std::string httpHeader = ":http-header=" + std::string(*keyStr) + ": " + std::string(*valueStr);
libvlc_media_add_option(media, httpHeader.c_str());
}
}
} else {
media = libvlc_media_new_path(inst, mediaPath.c_str());
}
if (!inst) {
inst = libvlc_new(0, NULL);
if (!inst) {
isolate->ThrowException(v8::Exception::Error(
String::NewFromUtf8(isolate, "Failed to initialize libVLC.", v8::NewStringType::kNormal).ToLocalChecked()
));
return;
}
}
if (!mp) {
mp = libvlc_media_player_new(inst);
if (!mp) {
isolate->ThrowException(v8::Exception::Error(
String::NewFromUtf8(isolate, "Failed to create media player.", v8::NewStringType::kNormal).ToLocalChecked()
));
return;
}
} else {
libvlc_media_player_stop(mp);
}
libvlc_media_player_set_media(mp, media);
libvlc_media_player_play(mp);
libvlc_media_release(media); // We release here, media player increases ref count when setting
}
调用方式:
播放本地文件:
javascriptvlcAddon.playMedia("D:\\path_to_your_file.mp4");
播放 HTTP/HTTPS URL(不带请求头):
javascriptvlcAddon.playMedia("http://example.com/video.mp4");
播放 HTTP/HTTPS URL(带请求头):
javascriptvlcAddon.playMedia("http://example.com/video.mp4", { Authorization: "Bearer YOUR_TOKEN_HERE", AnotherHeader: "HeaderValue", });
这样,你可以使用 PlayMedia
函数播放本地文件,或者通过 HTTP/HTTPS 播放媒体,并选择性地提供请求头信息。
PauseMedia
void PauseMedia(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
bool shouldToggle = true; // 默认为切换播放/暂停
bool shouldPause = false;
// 如果有参数并且为布尔值,则读取其值
if (args.Length() >= 1 && args[0]->IsBoolean()) {
shouldToggle = false;
shouldPause = args[0]->BooleanValue(isolate);
}
if (mp) {
if (shouldToggle) {
if (libvlc_media_player_is_playing(mp)) {
libvlc_media_player_pause(mp);
} else {
libvlc_media_player_play(mp);
}
} else {
if (shouldPause) {
if (libvlc_media_player_is_playing(mp)) {
libvlc_media_player_pause(mp);
}
} else {
if (libvlc_media_player_get_state(mp) == libvlc_Ended) {
libvlc_media_player_stop(mp);
libvlc_media_player_play(mp);
} else if (!libvlc_media_player_is_playing(mp)) {
libvlc_media_player_play(mp);
}
}
}
}
}
这样,不传入参数时,它会切换播放/暂停状态,如下:
vlcAddon.pauseMedia(); // 切换播放/暂停状态
而当传入true
或false
参数时,它将暂停或播放视频,如下:
vlcAddon.pauseMedia(true); // 暂停
vlcAddon.pauseMedia(false); // 播放
IsMediaEnded
要将IsMediaEnded
函数暴露给 Node.js,您需要遵循与其他函数相同的模式,即创建一个包装该函数的新 V8 函数并在模块初始化时将其导出。
这里是如何实现的:
- 首先,修改
IsMediaEnded
函数以使用 V8 的参数和返回值模式:
void IsMediaEnded(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
bool ended = false;
if (mp) {
ended = libvlc_media_player_get_state(mp) == libvlc_Ended;
}
args.GetReturnValue().Set(v8::Boolean::New(isolate, ended));
}
- 在模块的初始化部分,将这个新函数添加到导出的对象上:
void Initialize(Local<Object> exports) {
NODE_SET_METHOD(exports, "playMedia", PlayMedia);
NODE_SET_METHOD(exports, "pauseMedia", PauseMedia);
// ... 其他方法 ...
NODE_SET_METHOD(exports, "isMediaEnded", IsMediaEnded); // 这是新添加的方法
}
NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)
完成以上步骤后,您应该能够在 Node.js 中像调用其他函数一样调用isMediaEnded
函数,例如:
const vlcAddon = require("./build/Release/vlc_node_extension");
let ended = vlcAddon.isMediaEnded();
console.log("Is media ended?", ended);
请确保已经在文件的顶部包含了 V8 的所有相关头文件,如:
#include <node.h>
#include <v8.h>
注意:为了确保一切正常,您可能还需要重新编译 addon。
如何将外部的应用嵌入到 electron 应用中
检索关键字:Electron NativeView overlay
https://github.com/electron/electron/issues/10547
"build": {
"extraResources": [
{
"from": "./vlc-3.0.9.2",
"to": "vlc-3.0.9.2"
},
{
"from": "./bin/win32-x64-116",
"to": "vlc-3.0.9.2"
}
],
"files": [
"**/*",
"!bin/**/*",
"!build/**/*",
"!vlc-3.0.9.2/**/*",
"!path_to_some_media_file.mp4"
]
},
electron rebuild 生成 .node 文件
https://www.npmjs.com/package/@electron/rebuild
npm install --save-dev @electron/rebuild
.\node_modules\.bin\electron-rebuild.cmd
Mac 中如何开发
https://get.videolan.org/vlc/3.0.9.2/macosx/
- 点击 下载 Mac VLC 安装包
https://get.videolan.org/vlc/3.0.9.2/macosx/vlc-3.0.9.2.dmg
- binding.gyp 中指定 vlc 的 sdk 路径
{
"targets": [
{
"target_name": "vlc_node_extension",
"sources": ["main.cc"],
"conditions": [
[
"OS=='win'",
{
"include_dirs": [
"C:/Users/Administrator/Desktop/electron-vlc/vlc-3.0.9.2/sdk/include"
],
"libraries": [
"C:/Users/Administrator/Desktop/electron-vlc/vlc-3.0.9.2/sdk/lib/libvlc.lib",
"C:/Users/Administrator/Desktop/electron-vlc/vlc-3.0.9.2/sdk/lib/libvlccore.lib"
]
}
],
[
"OS=='mac'",
{
"include_dirs": ["/Applications/VLC.app/Contents/MacOS/include"],
"libraries": [
"-L/Applications/VLC.app/Contents/MacOS/lib",
"-lvlc",
"-lvlccore"
],
"xcode_settings": {
"OTHER_LDFLAGS": ["-framework CoreFoundation", "-framework Cocoa"]
}
}
],
[
"OS=='linux'",
{
"include_dirs": ["<!@(pkg-config --cflags libvlc)"],
"libraries": ["<!@(pkg-config --libs libvlc)"]
}
]
]
}
]
}
- 运行下面命令 生成 .node 文件
npm run rebuild
生成 bin
、build
目录就可以了
主进程中 配置 vlc 的路径
更新 xcode
dyld[60482]: missing symbol called
[60485:1012/195958.046143:ERROR:child_thread_impl.cc(235)] Invalid PlatformChannel receive right
/Users/shaohai.li/project/electron-vlc/node_modules/electron/dist/Electron.app/Contents/MacOS/Electron exited with signal SIGABRT
shaohai.li@192 electron-vlc %