#include #include #include #include #include #include #include #include #include #include #include // グローバル変数 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", "0"); 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(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(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)); } } void display_mpv_version(mpv_handle* mpv) { const char* version = mpv_get_property_string(mpv, "mpv-version"); if (version) { log("MPV Version: " + std::string(version)); mpv_free((void*)version); } else { log("Failed to get MPV version"); } } void display_vapoursynth_info(mpv_handle* mpv) { char* vs_script = nullptr; int 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"); } } void display_video_filters(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))); } } void display_all_info(mpv_handle* mpv) { display_mpv_version(mpv); display_vapoursynth_info(mpv); display_video_filters(mpv); } // ウィンドウプロシージャ 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 == 'I' || wParam == 'i') { // 'I'キーで情報表示 display_all_info(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; } }