mpv でVapoursynthが使えない
Q&A
Closed
MSYS2とVS2022でビルドしたMPV プレイヤーでVapoursynthが使えません。
Vapoursynth付きのコンパイルをしているのですがログファイルには[cplayer] Option vf-toggle: 'vapoursynth' isn't supported. となってしまいます。
普通のMPVプレイヤーなら動きます。 どなたか動いた方、知っている方いましたら回答お願いいたします。
Vapoursynth R69
libmpv バージョン mpv v0.38.0-623-ged77616f29 Copyright ツゥ 2000-2024 mpv/MPlayer/mplayer2 projects
#include <iostream>
#include <thread>
#include <chrono>
#include <stdexcept>
#include <windows.h>
#include <mpv/client.h>
#include <mpv/render.h>
#include <ctime>
#include <string>
#include <codecvt>
#include <locale>
// グローバル変数
mpv_handle* g_mpv = nullptr;
mpv_render_context* g_mpv_ctx = nullptr;
bool g_running = true;
HWND g_hwnd = nullptr;
bool vapoursynth_enabled = false; // VapourSynthの有効/無効状態を追跡
// UTF-8変換関数
std::string utf8_encode(const std::wstring& wstr) {
if (wstr.empty()) return std::string();
int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL);
std::string strTo(size_needed, 0);
WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL);
return strTo;
}
// ログ関数
void log(const std::string& msg) {
std::time_t now = std::time(nullptr);
char buf[20];
std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", std::localtime(&now));
std::cerr << "[" << buf << "] " << msg << std::endl;
OutputDebugStringA((msg + "\n").c_str());
}
// 例外ハンドラ
void handle_exception(const std::exception& e) {
log("Exception caught: " + std::string(e.what()));
MessageBoxA(NULL, e.what(), "Error", MB_OK | MB_ICONERROR);
}
// VapourSynthフィルターのトグル関数
void debug_vapoursynth_toggle(mpv_handle* mpv) {
char* vf_string = nullptr;
int error = mpv_get_property(mpv, "vf", MPV_FORMAT_STRING, &vf_string);
if (error >= 0 && vf_string) {
log("Current video filters: " + std::string(vf_string));
mpv_free(vf_string);
}
else {
log("Failed to get current video filters: " + std::string(mpv_error_string(error)));
}
// VapourSynthフィルターの状態を確認
char* vs_script = nullptr;
error = mpv_get_property(mpv, "vf-metadata/vapoursynth/file", MPV_FORMAT_STRING, &vs_script);
if (error >= 0 && vs_script) {
log("VapourSynth script: " + std::string(vs_script));
mpv_free(vs_script);
}
else {
log("No VapourSynth script currently active");
}
// フィルターの切り替えを試みる
const char* cmd[] = { "vf", "toggle", "vapoursynth=~~/vs/MDegrain3.vpy", NULL };
error = mpv_command(mpv, cmd);
if (error < 0) {
log("Failed to toggle VapourSynth filter: " + std::string(mpv_error_string(error)));
}
else {
vapoursynth_enabled = !vapoursynth_enabled;
log("VapourSynth filter toggled successfully. New state: " + std::string(vapoursynth_enabled ? "Enabled" : "Disabled"));
}
// 切り替え後の状態を再度確認
error = mpv_get_property(mpv, "vf", MPV_FORMAT_STRING, &vf_string);
if (error >= 0 && vf_string) {
log("Video filters after toggle: " + std::string(vf_string));
mpv_free(vf_string);
}
}
// MPVイベントハンドラ
void handle_mpv_event(mpv_event* event) {
switch (event->event_id) {
case MPV_EVENT_LOG_MESSAGE: {
mpv_event_log_message* msg = (mpv_event_log_message*)event->data;
log(std::string(msg->prefix) + ": " + std::string(msg->text));
break;
}
case MPV_EVENT_PROPERTY_CHANGE: {
mpv_event_property* prop = (mpv_event_property*)event->data;
if (strcmp(prop->name, "video-params") == 0) {
log("Video parameters changed");
}
break;
}
default:
break;
}
}
// MPV初期化関数
bool init_mpv(HWND hwnd) {
try {
log("Initializing MPV");
g_mpv = mpv_create();
if (!g_mpv) throw std::runtime_error("Failed to create MPV handle");
mpv_set_option_string(g_mpv, "vo", "gpu");
mpv_set_option_string(g_mpv, "vf", "");
mpv_set_option_string(g_mpv, "ao", "wasapi");
mpv_set_option_string(g_mpv, "hwdec", "no");
mpv_set_option_string(g_mpv, "video-sync", "audio");
mpv_set_option_string(g_mpv, "input-default-bindings", "yes");
mpv_set_option_string(g_mpv, "input-vo-keyboard", "yes");
mpv_set_option_string(g_mpv, "osc", "yes");
mpv_set_option_string(g_mpv, "osd-bar-align-y", "0.8");
mpv_set_option_string(g_mpv, "osd-bar-h", "2");
mpv_set_option_string(g_mpv, "osd-bar-w", "75");
mpv_set_option_string(g_mpv, "framedrop", "vo");
mpv_set_option_string(g_mpv, "osd-bar", "yes");
mpv_set_option_string(g_mpv, "osd-color", "FFFFFF");
mpv_set_option_string(g_mpv, "osd-back-color", "000000");
mpv_set_option_string(g_mpv, "osd-font-size", "30");
mpv_set_option_string(g_mpv, "osd-on-seek", "bar");
mpv_set_option_string(g_mpv, "osd-duration", "1000");
mpv_set_option_string(g_mpv, "osd-level", "1");
mpv_set_option_string(g_mpv, "config", "yes");
wchar_t exePath[MAX_PATH];
GetModuleFileNameW(NULL, exePath, MAX_PATH);
std::wstring exeDir = std::wstring(exePath);
exeDir = exeDir.substr(0, exeDir.find_last_of(L"\\/"));
std::string utf8ExeDir = utf8_encode(exeDir);
std::string inputConfPath = utf8ExeDir + "\\input.conf";
std::string mpvConfPath = utf8ExeDir + "\\mpv.conf";
mpv_set_option_string(g_mpv, "input-conf", inputConfPath.c_str());
mpv_set_option_string(g_mpv, "config-dir", utf8ExeDir.c_str());
int64_t yes = 1;
mpv_set_option(g_mpv, "osc", MPV_FORMAT_FLAG, &yes);
mpv_request_log_messages(g_mpv, "v");
if (mpv_initialize(g_mpv) < 0)
throw std::runtime_error("Failed to initialize MPV");
log("MPV initialized successfully");
mpv_render_param params[] = {
{MPV_RENDER_PARAM_API_TYPE, const_cast<char*>(MPV_RENDER_API_TYPE_SW)},
{MPV_RENDER_PARAM_INVALID, nullptr}
};
int render_status = mpv_render_context_create(&g_mpv_ctx, g_mpv, params);
if (render_status < 0) {
const char* error = mpv_error_string(render_status);
throw std::runtime_error("Failed to create MPV render context: " + std::string(error));
}
log("MPV render context created successfully");
mpv_observe_property(g_mpv, 0, "video-params", MPV_FORMAT_NODE);
mpv_observe_property(g_mpv, 0, "audio-params", MPV_FORMAT_NODE);
const char* version = mpv_get_property_string(g_mpv, "mpv-version");
const char* config = mpv_get_property_string(g_mpv, "libmpv-config");
log("MPV Version: " + std::string(version ? version : "unknown"));
log("MPV Config: " + std::string(config ? config : "unknown"));
mpv_free((void*)version);
mpv_free((void*)config);
return true;
}
catch (const std::exception& e) {
handle_exception(e);
return false;
}
}
// ファイル再生関数
void play_file(const wchar_t* filepath) {
if (!g_mpv) {
log("MPV not initialized");
return;
}
try {
std::string utf8_path = utf8_encode(filepath);
log("Attempting to play file: " + utf8_path);
const char* cmd[] = { "loadfile", utf8_path.c_str(), NULL };
int error = mpv_command(g_mpv, cmd);
if (error < 0) {
throw std::runtime_error("Error playing file: " + std::string(mpv_error_string(error)));
}
log("File loaded successfully");
mpv_observe_property(g_mpv, 0, "pause", MPV_FORMAT_FLAG);
}
catch (const std::exception& e) {
handle_exception(e);
}
}
// レンダリングループ
void render_loop() {
while (g_running) {
try {
if (g_mpv_ctx) {
mpv_event* event = mpv_wait_event(g_mpv, 0);
if (event->event_id == MPV_EVENT_SHUTDOWN) {
g_running = false;
break;
}
handle_mpv_event(event);
uint64_t flags = mpv_render_context_update(g_mpv_ctx);
if (flags & MPV_RENDER_UPDATE_FRAME) {
RECT rect;
GetClientRect(g_hwnd, &rect);
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
HDC hdc = GetDC(g_hwnd);
BITMAPINFO bmi = { 0 };
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = width;
bmi.bmiHeader.biHeight = -height;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
void* pixels = nullptr;
HBITMAP hBitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &pixels, NULL, 0);
if (hBitmap && pixels) {
int size[2] = { width, height };
mpv_render_param params[] = {
{MPV_RENDER_PARAM_SW_SIZE, size},
{MPV_RENDER_PARAM_SW_FORMAT, const_cast<char*>(MPV_RENDER_API_TYPE_SW)},
{MPV_RENDER_PARAM_SW_STRIDE, &width},
{MPV_RENDER_PARAM_SW_POINTER, pixels},
{MPV_RENDER_PARAM_INVALID, nullptr}
};
mpv_render_context_render(g_mpv_ctx, params);
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hbmOld = (HBITMAP)SelectObject(hdcMem, hBitmap);
BitBlt(hdc, 0, 0, width, height, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, hbmOld);
DeleteDC(hdcMem);
DeleteObject(hBitmap);
}
ReleaseDC(g_hwnd, hdc);
}
}
}
catch (const std::exception& e) {
handle_exception(e);
}
std::this_thread::sleep_for(std::chrono::milliseconds(16));
}
}
// ウィンドウプロシージャ
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_CLOSE:
g_running = false;
PostQuitMessage(0);
return 0;
case WM_LBUTTONDOWN:
{
wchar_t filepath[MAX_PATH] = { 0 };
OPENFILENAMEW ofn = { 0 };
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hwnd;
ofn.lpstrFile = filepath;
ofn.nMaxFile = sizeof(filepath) / sizeof(wchar_t);
ofn.lpstrFilter = L"All\0*.*\0";
ofn.nFilterIndex = 1;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
if (GetOpenFileNameW(&ofn)) {
play_file(filepath);
}
}
return 0;
case WM_KEYDOWN:
if (wParam == '1') { // '1'キーでVapourSynthフィルターをトグル
debug_vapoursynth_toggle(g_mpv);
}
return 0;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
try {
log("Application started");
WNDCLASSW wc = { 0 };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = L"MPVPlayerClass";
if (!RegisterClassW(&wc))
throw std::runtime_error("Failed to register window class");
g_hwnd = CreateWindowExW(
0, L"MPVPlayerClass", L"MPV Player",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
nullptr, nullptr, hInstance, nullptr
);
if (!g_hwnd) throw std::runtime_error("Failed to create window");
ShowWindow(g_hwnd, nCmdShow);
if (!init_mpv(g_hwnd))
throw std::runtime_error("Failed to initialize MPV");
std::thread render_thread(render_loop);
MSG msg;
while (GetMessageW(&msg, nullptr, 0, 0)) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
g_running = false;
render_thread.join();
if (g_mpv_ctx) mpv_render_context_free(g_mpv_ctx);
if (g_mpv) mpv_destroy(g_mpv);
log("Application ended normally");
return 0;
}
catch (const std::exception& e) {
handle_exception(e);
log("Application ended with an error");
return 1;
}
}
0