fix(mpv): avoid blocking runtime calls
This commit is contained in:
@@ -82,13 +82,25 @@ bool MpvPlayer::Initialize(HWND container, HWND flutter_window) {
|
||||
void MpvPlayer::Dispose() {
|
||||
StopEventLoop();
|
||||
|
||||
// Cancel pending async commands
|
||||
// Cancel pending async requests
|
||||
std::vector<StatusCallback> status_callbacks;
|
||||
std::vector<GetPropertyCallback> get_callbacks;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(pending_commands_mutex_);
|
||||
for (auto& pair : pending_commands_) {
|
||||
if (pair.second) pair.second(-1); // Call with error
|
||||
std::lock_guard<std::mutex> lock(pending_requests_mutex_);
|
||||
for (auto& pair : pending_status_requests_) {
|
||||
if (pair.second) status_callbacks.push_back(std::move(pair.second));
|
||||
}
|
||||
pending_commands_.clear();
|
||||
for (auto& pair : pending_get_property_requests_) {
|
||||
if (pair.second) get_callbacks.push_back(std::move(pair.second));
|
||||
}
|
||||
pending_status_requests_.clear();
|
||||
pending_get_property_requests_.clear();
|
||||
}
|
||||
for (auto& callback : status_callbacks) {
|
||||
callback(-1);
|
||||
}
|
||||
for (auto& callback : get_callbacks) {
|
||||
callback(-1, "");
|
||||
}
|
||||
|
||||
if (mpv_) {
|
||||
@@ -104,18 +116,7 @@ void MpvPlayer::Dispose() {
|
||||
observed_properties_.clear();
|
||||
}
|
||||
|
||||
void MpvPlayer::Command(const std::vector<std::string>& args) {
|
||||
if (!mpv_) return;
|
||||
|
||||
std::vector<const char*> c_args;
|
||||
c_args.reserve(args.size() + 1);
|
||||
for (const auto& arg : args) {
|
||||
c_args.push_back(arg.c_str());
|
||||
}
|
||||
c_args.push_back(nullptr);
|
||||
|
||||
mpv_command(mpv_, c_args.data());
|
||||
}
|
||||
void MpvPlayer::Command(const std::vector<std::string>& args) { CommandAsync(args, nullptr); }
|
||||
|
||||
void MpvPlayer::CommandAsync(const std::vector<std::string>& args, CommandCallback callback) {
|
||||
if (!mpv_) {
|
||||
@@ -130,50 +131,88 @@ void MpvPlayer::CommandAsync(const std::vector<std::string>& args, CommandCallba
|
||||
}
|
||||
c_args.push_back(nullptr);
|
||||
|
||||
// Generate unique request ID and store callback
|
||||
uint64_t request_id;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(pending_commands_mutex_);
|
||||
request_id = next_reply_userdata_++;
|
||||
pending_commands_[request_id] = std::move(callback);
|
||||
}
|
||||
uint64_t request_id = callback ? RegisterStatusRequest(std::move(callback)) : 0;
|
||||
|
||||
// mpv_command_async returns immediately
|
||||
int result = mpv_command_async(mpv_, request_id, c_args.data());
|
||||
if (result < 0) {
|
||||
// Submission failed, complete immediately with error
|
||||
std::lock_guard<std::mutex> lock(pending_commands_mutex_);
|
||||
auto it = pending_commands_.find(request_id);
|
||||
if (it != pending_commands_.end()) {
|
||||
auto cb = std::move(it->second);
|
||||
pending_commands_.erase(it);
|
||||
if (cb) cb(result);
|
||||
}
|
||||
auto cb = TakeStatusRequest(request_id);
|
||||
if (cb) cb(result);
|
||||
}
|
||||
}
|
||||
|
||||
void MpvPlayer::SetProperty(const std::string& name, const std::string& value) {
|
||||
if (!mpv_) return;
|
||||
SetPropertyAsync(name, value, nullptr);
|
||||
}
|
||||
|
||||
void MpvPlayer::SetPropertyAsync(const std::string& name, const std::string& value, StatusCallback callback) {
|
||||
if (!mpv_) {
|
||||
if (callback) callback(0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle custom HDR toggle property (same pattern as iOS/macOS)
|
||||
if (name == "hdr-enabled") {
|
||||
bool enabled = (value == "yes" || value == "true" || value == "1");
|
||||
SetHDREnabled(enabled);
|
||||
SetHDREnabled(enabled, std::move(callback));
|
||||
return;
|
||||
}
|
||||
|
||||
mpv_set_property_string(mpv_, name.c_str(), value.c_str());
|
||||
uint64_t request_id = callback ? RegisterStatusRequest(std::move(callback)) : 0;
|
||||
|
||||
char* property_value = const_cast<char*>(value.c_str());
|
||||
int result = mpv_set_property_async(mpv_, request_id, name.c_str(), MPV_FORMAT_STRING, &property_value);
|
||||
if (result < 0) {
|
||||
auto cb = TakeStatusRequest(request_id);
|
||||
if (cb) cb(result);
|
||||
}
|
||||
}
|
||||
|
||||
std::string MpvPlayer::GetProperty(const std::string& name) {
|
||||
if (!mpv_) return "";
|
||||
void MpvPlayer::GetPropertyAsync(const std::string& name, GetPropertyCallback callback) {
|
||||
if (!mpv_) {
|
||||
if (callback) callback(-1, "");
|
||||
return;
|
||||
}
|
||||
|
||||
char* value = mpv_get_property_string(mpv_, name.c_str());
|
||||
if (!value) return "";
|
||||
uint64_t request_id = RegisterGetPropertyRequest(std::move(callback));
|
||||
|
||||
std::string result(value);
|
||||
mpv_free(value);
|
||||
return result;
|
||||
int result = mpv_get_property_async(mpv_, request_id, name.c_str(), MPV_FORMAT_STRING);
|
||||
if (result < 0) {
|
||||
auto cb = TakeGetPropertyRequest(request_id);
|
||||
if (cb) cb(result, "");
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t MpvPlayer::RegisterStatusRequest(StatusCallback callback) {
|
||||
std::lock_guard<std::mutex> lock(pending_requests_mutex_);
|
||||
uint64_t request_id = next_reply_userdata_++;
|
||||
pending_status_requests_[request_id] = std::move(callback);
|
||||
return request_id;
|
||||
}
|
||||
|
||||
MpvPlayer::StatusCallback MpvPlayer::TakeStatusRequest(uint64_t request_id) {
|
||||
std::lock_guard<std::mutex> lock(pending_requests_mutex_);
|
||||
auto it = pending_status_requests_.find(request_id);
|
||||
if (it == pending_status_requests_.end()) return nullptr;
|
||||
auto callback = std::move(it->second);
|
||||
pending_status_requests_.erase(it);
|
||||
return callback;
|
||||
}
|
||||
|
||||
uint64_t MpvPlayer::RegisterGetPropertyRequest(GetPropertyCallback callback) {
|
||||
std::lock_guard<std::mutex> lock(pending_requests_mutex_);
|
||||
uint64_t request_id = next_reply_userdata_++;
|
||||
pending_get_property_requests_[request_id] = std::move(callback);
|
||||
return request_id;
|
||||
}
|
||||
|
||||
MpvPlayer::GetPropertyCallback MpvPlayer::TakeGetPropertyRequest(uint64_t request_id) {
|
||||
std::lock_guard<std::mutex> lock(pending_requests_mutex_);
|
||||
auto it = pending_get_property_requests_.find(request_id);
|
||||
if (it == pending_get_property_requests_.end()) return nullptr;
|
||||
auto callback = std::move(it->second);
|
||||
pending_get_property_requests_.erase(it);
|
||||
return callback;
|
||||
}
|
||||
|
||||
void MpvPlayer::ObserveProperty(const std::string& name, const std::string& format, int id) {
|
||||
@@ -287,23 +326,31 @@ void MpvPlayer::EventLoop() {
|
||||
|
||||
void MpvPlayer::HandleMpvEvent(mpv_event* event) {
|
||||
switch (event->event_id) {
|
||||
case MPV_EVENT_COMMAND_REPLY: {
|
||||
// Handle async command completion
|
||||
case MPV_EVENT_COMMAND_REPLY:
|
||||
case MPV_EVENT_SET_PROPERTY_REPLY: {
|
||||
uint64_t request_id = event->reply_userdata;
|
||||
CommandCallback callback;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(pending_commands_mutex_);
|
||||
auto it = pending_commands_.find(request_id);
|
||||
if (it != pending_commands_.end()) {
|
||||
callback = std::move(it->second);
|
||||
pending_commands_.erase(it);
|
||||
}
|
||||
}
|
||||
StatusCallback callback = TakeStatusRequest(request_id);
|
||||
if (callback) {
|
||||
callback(event->error);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MPV_EVENT_GET_PROPERTY_REPLY: {
|
||||
uint64_t request_id = event->reply_userdata;
|
||||
GetPropertyCallback callback = TakeGetPropertyRequest(request_id);
|
||||
if (callback) {
|
||||
std::string value;
|
||||
if (event->error >= 0) {
|
||||
auto* prop = static_cast<mpv_event_property*>(event->data);
|
||||
if (prop && prop->format == MPV_FORMAT_STRING && prop->data) {
|
||||
auto c_value = *static_cast<char**>(prop->data);
|
||||
if (c_value) value = SanitizeUtf8(c_value);
|
||||
}
|
||||
}
|
||||
callback(event->error, value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MPV_EVENT_LOG_MESSAGE: {
|
||||
auto* msg = static_cast<mpv_event_log_message*>(event->data);
|
||||
char log_msg[512];
|
||||
@@ -356,11 +403,14 @@ void MpvPlayer::HandleMpvEvent(mpv_event* event) {
|
||||
// to null output (e.g. after sleep/wake or device unplug), re-set
|
||||
// audio-device to switch back to the real output.
|
||||
// Mirrors mpv's TOOLS/lua/ao-null-reload.lua for embedded libmpv.
|
||||
if (strcmp(prop->name, "audio-device-list") == 0 && GetProperty("current-ao") == "null") {
|
||||
auto device = GetProperty("audio-device");
|
||||
if (!device.empty()) {
|
||||
mpv_set_property_string(mpv_, "audio-device", device.c_str());
|
||||
}
|
||||
if (strcmp(prop->name, "audio-device-list") == 0) {
|
||||
GetPropertyAsync("current-ao", [this](int ao_error, const std::string& current_ao) {
|
||||
if (ao_error < 0 || current_ao != "null") return;
|
||||
GetPropertyAsync("audio-device", [this](int device_error, const std::string& device) {
|
||||
if (device_error < 0 || device.empty()) return;
|
||||
SetProperty("audio-device", device);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
SendPropertyChange(prop->name, &node);
|
||||
@@ -445,11 +495,13 @@ void MpvPlayer::SendEvent(const std::string& name, const flutter::EncodableMap&
|
||||
}
|
||||
}
|
||||
|
||||
void MpvPlayer::SetHDREnabled(bool enabled) {
|
||||
void MpvPlayer::SetHDREnabled(bool enabled, StatusCallback callback) {
|
||||
hdr_enabled_ = enabled;
|
||||
|
||||
if (mpv_) {
|
||||
mpv_set_property_string(mpv_, "target-colorspace-hint", enabled ? "yes" : "no");
|
||||
SetPropertyAsync("target-colorspace-hint", enabled ? "yes" : "no", std::move(callback));
|
||||
} else if (callback) {
|
||||
callback(0);
|
||||
}
|
||||
|
||||
UpdateHDRMode(last_sig_peak_);
|
||||
|
||||
@@ -34,21 +34,25 @@ class MpvPlayer {
|
||||
// Returns true if mpv is initialized.
|
||||
bool IsInitialized() const { return mpv_ != nullptr; }
|
||||
|
||||
// Executes an mpv command.
|
||||
// Queues an mpv command without waiting for completion.
|
||||
void Command(const std::vector<std::string>& args);
|
||||
|
||||
// Callback type for async command completion.
|
||||
using CommandCallback = std::function<void(int error)>;
|
||||
// Callback types for async mpv requests.
|
||||
using StatusCallback = std::function<void(int error)>;
|
||||
using CommandCallback = StatusCallback;
|
||||
using GetPropertyCallback = std::function<void(int error, const std::string& value)>;
|
||||
|
||||
// Executes an mpv command asynchronously to prevent UI blocking.
|
||||
// The callback is called on the main thread when the command completes.
|
||||
void CommandAsync(const std::vector<std::string>& args, CommandCallback callback);
|
||||
|
||||
// Sets an mpv property.
|
||||
// Queues an mpv property update without waiting for completion.
|
||||
void SetProperty(const std::string& name, const std::string& value);
|
||||
|
||||
// Gets an mpv property.
|
||||
std::string GetProperty(const std::string& name);
|
||||
// Sets an mpv property asynchronously.
|
||||
void SetPropertyAsync(const std::string& name, const std::string& value, StatusCallback callback);
|
||||
|
||||
// Gets an mpv property asynchronously.
|
||||
void GetPropertyAsync(const std::string& name, GetPropertyCallback callback);
|
||||
|
||||
// Observes an mpv property for changes.
|
||||
void ObserveProperty(const std::string& name, const std::string& format, int id);
|
||||
@@ -75,6 +79,10 @@ class MpvPlayer {
|
||||
void HandleMpvEvent(mpv_event* event);
|
||||
void SendPropertyChange(const char* name, mpv_node* data);
|
||||
void SendEvent(const std::string& name, const flutter::EncodableMap& data = {});
|
||||
uint64_t RegisterStatusRequest(StatusCallback callback);
|
||||
StatusCallback TakeStatusRequest(uint64_t request_id);
|
||||
uint64_t RegisterGetPropertyRequest(GetPropertyCallback callback);
|
||||
GetPropertyCallback TakeGetPropertyRequest(uint64_t request_id);
|
||||
|
||||
mpv_handle* mpv_ = nullptr;
|
||||
HWND hwnd_ = nullptr;
|
||||
@@ -92,16 +100,17 @@ class MpvPlayer {
|
||||
std::map<std::string, uint64_t> observed_properties_;
|
||||
std::map<std::string, int> name_to_id_;
|
||||
|
||||
// Pending async commands: request_id -> callback
|
||||
std::map<uint64_t, CommandCallback> pending_commands_;
|
||||
std::mutex pending_commands_mutex_;
|
||||
// Pending async requests: request_id -> callback
|
||||
std::map<uint64_t, StatusCallback> pending_status_requests_;
|
||||
std::map<uint64_t, GetPropertyCallback> pending_get_property_requests_;
|
||||
std::mutex pending_requests_mutex_;
|
||||
|
||||
// HDR state
|
||||
bool hdr_enabled_ = true; // User preference
|
||||
double last_sig_peak_ = 0.0; // Last known sig-peak for HDR content detection
|
||||
|
||||
// HDR methods
|
||||
void SetHDREnabled(bool enabled);
|
||||
void SetHDREnabled(bool enabled, StatusCallback callback = nullptr);
|
||||
void UpdateHDRMode(double sigPeak);
|
||||
};
|
||||
|
||||
|
||||
@@ -18,12 +18,17 @@ void MpvPlayerPluginRegisterWithRegistrar(FlutterDesktopPluginRegistrarRef regis
|
||||
|
||||
namespace mpv {
|
||||
|
||||
namespace {
|
||||
constexpr UINT kPlatformTaskMessage = WM_APP + 0x4D50;
|
||||
}
|
||||
|
||||
void MpvPlayerPlugin::RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar) {
|
||||
auto plugin = std::make_unique<MpvPlayerPlugin>(registrar);
|
||||
registrar->AddPlugin(std::move(plugin));
|
||||
}
|
||||
|
||||
MpvPlayerPlugin::MpvPlayerPlugin(flutter::PluginRegistrarWindows* registrar) : registrar_(registrar) {
|
||||
MpvPlayerPlugin::MpvPlayerPlugin(flutter::PluginRegistrarWindows* registrar)
|
||||
: registrar_(registrar), platform_thread_id_(::GetCurrentThreadId()) {
|
||||
// Create method channel.
|
||||
method_channel_ = std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(
|
||||
registrar->messenger(), "com.plezy/mpv_player", &flutter::StandardMethodCodec::GetInstance());
|
||||
@@ -53,6 +58,8 @@ MpvPlayerPlugin::MpvPlayerPlugin(flutter::PluginRegistrarWindows* registrar) : r
|
||||
}
|
||||
|
||||
MpvPlayerPlugin::~MpvPlayerPlugin() {
|
||||
DrainPlatformTasks();
|
||||
|
||||
// Unregister window proc delegate.
|
||||
if (proc_id_) {
|
||||
registrar_->UnregisterTopLevelWindowProcDelegate(proc_id_.value());
|
||||
@@ -64,6 +71,35 @@ HWND MpvPlayerPlugin::GetChildWindow() { return registrar_->GetView()->GetNative
|
||||
|
||||
HWND MpvPlayerPlugin::GetWindow() { return ::GetAncestor(GetChildWindow(), GA_ROOT); }
|
||||
|
||||
void MpvPlayerPlugin::PostToPlatformThread(std::function<void()> task) {
|
||||
if (::GetCurrentThreadId() == platform_thread_id_) {
|
||||
task();
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(platform_tasks_mutex_);
|
||||
platform_tasks_.push(std::move(task));
|
||||
}
|
||||
|
||||
if (flutter_window_) {
|
||||
::PostMessage(flutter_window_, kPlatformTaskMessage, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void MpvPlayerPlugin::DrainPlatformTasks() {
|
||||
std::queue<std::function<void()>> tasks;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(platform_tasks_mutex_);
|
||||
tasks.swap(platform_tasks_);
|
||||
}
|
||||
|
||||
while (!tasks.empty()) {
|
||||
tasks.front()();
|
||||
tasks.pop();
|
||||
}
|
||||
}
|
||||
|
||||
void MpvPlayerPlugin::HandleMethodCall(
|
||||
const flutter::MethodCall<flutter::EncodableValue>& method_call,
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
|
||||
@@ -77,11 +113,17 @@ void MpvPlayerPlugin::HandleMethodCall(
|
||||
}
|
||||
|
||||
HWND flutter_window = GetWindow();
|
||||
flutter_window_ = flutter_window;
|
||||
|
||||
MpvCore::SetInstance(std::make_unique<MpvCore>(flutter_window));
|
||||
|
||||
proc_id_ =
|
||||
registrar_->RegisterTopLevelWindowProcDelegate([](HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
|
||||
registrar_->RegisterTopLevelWindowProcDelegate([this](HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
|
||||
if (message == kPlatformTaskMessage) {
|
||||
DrainPlatformTasks();
|
||||
return std::optional<HRESULT>(0);
|
||||
}
|
||||
|
||||
auto* core = MpvCore::GetInstance();
|
||||
if (core) {
|
||||
return core->WindowProc(hwnd, message, wparam, lparam);
|
||||
@@ -160,13 +202,15 @@ void MpvPlayerPlugin::HandleMethodCall(
|
||||
auto result_ptr =
|
||||
std::make_shared<std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>>>(std::move(result));
|
||||
std::string cmd_name = command_args.empty() ? "unknown" : command_args[0];
|
||||
player_->CommandAsync(command_args, [result_ptr, cmd_name](int error) {
|
||||
if (error < 0) {
|
||||
(*result_ptr)
|
||||
->Error("COMMAND_FAILED", "MPV command failed: " + cmd_name + " (error " + std::to_string(error) + ")");
|
||||
} else {
|
||||
(*result_ptr)->Success();
|
||||
}
|
||||
player_->CommandAsync(command_args, [this, result_ptr, cmd_name](int error) {
|
||||
PostToPlatformThread([result_ptr, cmd_name, error]() {
|
||||
if (error < 0) {
|
||||
(*result_ptr)
|
||||
->Error("COMMAND_FAILED", "MPV command failed: " + cmd_name + " (error " + std::to_string(error) + ")");
|
||||
} else {
|
||||
(*result_ptr)->Success();
|
||||
}
|
||||
});
|
||||
});
|
||||
return; // Response will be sent asynchronously
|
||||
} else if (method == "setProperty") {
|
||||
@@ -194,8 +238,12 @@ void MpvPlayerPlugin::HandleMethodCall(
|
||||
return;
|
||||
}
|
||||
|
||||
player_->SetProperty(std::get<std::string>(name_it->second), std::get<std::string>(value_it->second));
|
||||
result->Success();
|
||||
auto result_ptr =
|
||||
std::make_shared<std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>>>(std::move(result));
|
||||
player_->SetPropertyAsync(
|
||||
std::get<std::string>(name_it->second), std::get<std::string>(value_it->second),
|
||||
[this, result_ptr](int error) { PostToPlatformThread([result_ptr]() { (*result_ptr)->Success(); }); });
|
||||
return;
|
||||
} else if (method == "setLogLevel") {
|
||||
if (!player_ || !player_->IsInitialized()) {
|
||||
result->Error("NOT_INITIALIZED", "Player not initialized");
|
||||
@@ -238,12 +286,19 @@ void MpvPlayerPlugin::HandleMethodCall(
|
||||
return;
|
||||
}
|
||||
|
||||
std::string value = player_->GetProperty(std::get<std::string>(name_it->second));
|
||||
if (value.empty()) {
|
||||
result->Success();
|
||||
} else {
|
||||
result->Success(flutter::EncodableValue(value));
|
||||
}
|
||||
auto result_ptr =
|
||||
std::make_shared<std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>>>(std::move(result));
|
||||
player_->GetPropertyAsync(
|
||||
std::get<std::string>(name_it->second), [this, result_ptr](int error, const std::string& value) {
|
||||
PostToPlatformThread([result_ptr, error, value]() {
|
||||
if (error < 0 || value.empty()) {
|
||||
(*result_ptr)->Success();
|
||||
} else {
|
||||
(*result_ptr)->Success(flutter::EncodableValue(value));
|
||||
}
|
||||
});
|
||||
});
|
||||
return;
|
||||
} else if (method == "observeProperty") {
|
||||
if (!player_ || !player_->IsInitialized()) {
|
||||
result->Error("NOT_INITIALIZED", "Player not initialized");
|
||||
|
||||
@@ -8,8 +8,11 @@
|
||||
#include <flutter/plugin_registrar_windows.h>
|
||||
#include <flutter/standard_method_codec.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <queue>
|
||||
|
||||
#include "display_mode_manager.h"
|
||||
#include "mpv_core.h"
|
||||
@@ -33,11 +36,15 @@ class MpvPlayerPlugin : public flutter::Plugin {
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
|
||||
|
||||
void SendEvent(const flutter::EncodableValue& event);
|
||||
void PostToPlatformThread(std::function<void()> task);
|
||||
void DrainPlatformTasks();
|
||||
|
||||
HWND GetWindow();
|
||||
HWND GetChildWindow();
|
||||
|
||||
flutter::PluginRegistrarWindows* registrar_;
|
||||
DWORD platform_thread_id_;
|
||||
HWND flutter_window_ = nullptr;
|
||||
std::unique_ptr<flutter::MethodChannel<flutter::EncodableValue>> method_channel_;
|
||||
std::unique_ptr<flutter::EventChannel<flutter::EncodableValue>> event_channel_;
|
||||
std::unique_ptr<flutter::EventSink<flutter::EncodableValue>> event_sink_;
|
||||
@@ -45,6 +52,8 @@ class MpvPlayerPlugin : public flutter::Plugin {
|
||||
std::unique_ptr<MpvPlayer> player_;
|
||||
DisplayModeManager display_mode_manager_;
|
||||
std::optional<int32_t> proc_id_;
|
||||
std::mutex platform_tasks_mutex_;
|
||||
std::queue<std::function<void()>> platform_tasks_;
|
||||
};
|
||||
|
||||
} // namespace mpv
|
||||
|
||||
Reference in New Issue
Block a user