Compare commits
12 Commits
release-v5
...
release
| Author | SHA1 | Date | |
|---|---|---|---|
| 80aad703a0 | |||
| a766ed930c | |||
| 72163929a7 | |||
| f7e25fb4fe | |||
| c50a56d76c | |||
| 2556e8c3df | |||
| dc54385d71 | |||
| 08698861b2 | |||
| 2c935b29be | |||
| 60ae578648 | |||
| 6dca7c6029 | |||
| 524804d0eb |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
build/
|
build/
|
||||||
.vscode/
|
.vscode/
|
||||||
|
bin/
|
||||||
|
|||||||
@@ -8,7 +8,27 @@ endif()
|
|||||||
|
|
||||||
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/include")
|
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||||
|
|
||||||
|
option(USE_LIBMPV "Use libmpv." ON)
|
||||||
|
option(USE_PLAYER "Use player." OFF)
|
||||||
|
option(STATIC_CRT "Use static CRT" ON)
|
||||||
|
|
||||||
|
if (STATIC_CRT)
|
||||||
|
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (USE_LIBMPV AND USE_PLAYER)
|
||||||
|
message(FATAL_ERROR "USE_LIBMPV and USE_PLAYER cannot be both enabled.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (USE_LIBMPV)
|
||||||
|
set(HAVE_MPV 1)
|
||||||
|
elseif (USE_PLAYER)
|
||||||
|
set(HAVE_PLAYER 1)
|
||||||
|
endif()
|
||||||
|
|
||||||
set(DETOURS_LIB "${CMAKE_CURRENT_SOURCE_DIR}/lib/detours.lib")
|
set(DETOURS_LIB "${CMAKE_CURRENT_SOURCE_DIR}/lib/detours.lib")
|
||||||
|
set(PLAYER_LIB "${CMAKE_CURRENT_SOURCE_DIR}/lib/player.lib")
|
||||||
|
set(LIBMPV_LIB "${CMAKE_CURRENT_SOURCE_DIR}/lib/libmpv.dll.a")
|
||||||
|
|
||||||
set(ENABLE_ICONV OFF CACHE BOOL "Libiconv is not needed.")
|
set(ENABLE_ICONV OFF CACHE BOOL "Libiconv is not needed.")
|
||||||
add_subdirectory(utils)
|
add_subdirectory(utils)
|
||||||
@@ -37,9 +57,18 @@ set(ZLIB_ROOT "${CMAKE_CURRENT_SOURCE_DIR}" CACHE PATH "Zlib is needed.")
|
|||||||
find_package(ZLIB REQUIRED)
|
find_package(ZLIB REQUIRED)
|
||||||
add_subdirectory("libzip")
|
add_subdirectory("libzip")
|
||||||
|
|
||||||
|
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/patch_config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/patch_config.h")
|
||||||
|
include_directories("${CMAKE_CURRENT_BINARY_DIR}")
|
||||||
|
|
||||||
add_library(jewena_patch SHARED dllmain.cpp config.hpp config.cpp vfs.hpp vfs.cpp string_replace_file.hpp string_replace_file.cpp)
|
add_library(jewena_patch SHARED dllmain.cpp config.hpp config.cpp vfs.hpp vfs.cpp string_replace_file.hpp string_replace_file.cpp)
|
||||||
target_link_libraries(jewena_patch "${DETOURS_LIB}")
|
target_link_libraries(jewena_patch "${DETOURS_LIB}")
|
||||||
|
if (USE_LIBMPV)
|
||||||
|
target_link_libraries(jewena_patch "${LIBMPV_LIB}")
|
||||||
|
elseif (USE_PLAYER)
|
||||||
|
target_link_libraries(jewena_patch "${PLAYER_LIB}")
|
||||||
|
endif()
|
||||||
target_link_libraries(jewena_patch utils)
|
target_link_libraries(jewena_patch utils)
|
||||||
target_link_libraries(jewena_patch zip)
|
target_link_libraries(jewena_patch zip)
|
||||||
|
|
||||||
add_executable(jewena-chs WIN32 main.cpp winres.rc jewena-chs.exe.manifest)
|
add_executable(jewena-chs WIN32 main.cpp winres.rc jewena-chs.exe.manifest)
|
||||||
|
target_link_libraries(jewena-chs utils)
|
||||||
|
|||||||
67
config.cpp
67
config.cpp
@@ -2,6 +2,8 @@
|
|||||||
#include "fileop.h"
|
#include "fileop.h"
|
||||||
#include "file_reader.h"
|
#include "file_reader.h"
|
||||||
#include "malloc.h"
|
#include "malloc.h"
|
||||||
|
#include "str_util.h"
|
||||||
|
#include "player.h"
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
@@ -40,9 +42,74 @@ bool Config::Load(std::string path) {
|
|||||||
}
|
}
|
||||||
std::string key = l.substr(0, eq_pos);
|
std::string key = l.substr(0, eq_pos);
|
||||||
std::string value = l.substr(eq_pos + 1);
|
std::string value = l.substr(eq_pos + 1);
|
||||||
|
#if HAVE_MPV
|
||||||
|
if (key.find("mpv-") == 0) {
|
||||||
|
key = key.substr(4);
|
||||||
|
if (str_util::tolower(key) == "wid") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
mpv.push_back(std::make_pair(key, value));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
configs[key] = value;
|
configs[key] = value;
|
||||||
}
|
}
|
||||||
free_file_reader(reader);
|
free_file_reader(reader);
|
||||||
fileop::fclose(f);
|
fileop::fclose(f);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if HAVE_PLAYER
|
||||||
|
|
||||||
|
bool Config::IsAppendLogging() {
|
||||||
|
auto re = configs.find("appendLogging");
|
||||||
|
if (re == configs.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return str_util::parse_bool((*re).second);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Config::LoggingLevel() {
|
||||||
|
auto re = configs.find("loggingLevel");
|
||||||
|
if (re == configs.end()) {
|
||||||
|
return AV_LOG_INFO;
|
||||||
|
}
|
||||||
|
std::string level = str_util::tolower((*re).second);
|
||||||
|
if (level == "debug") {
|
||||||
|
return AV_LOG_DEBUG;
|
||||||
|
} else if (level == "verbose") {
|
||||||
|
return AV_LOG_VERBOSE;
|
||||||
|
} else if (level == "info") {
|
||||||
|
return AV_LOG_INFO;
|
||||||
|
} else if (level == "warning") {
|
||||||
|
return AV_LOG_WARNING;
|
||||||
|
} else if (level == "error") {
|
||||||
|
return AV_LOG_ERROR;
|
||||||
|
} else if (level == "fatal") {
|
||||||
|
return AV_LOG_FATAL;
|
||||||
|
} else if (level == "trace") {
|
||||||
|
return AV_LOG_TRACE;
|
||||||
|
} else if (level == "panic") {
|
||||||
|
return AV_LOG_PANIC;
|
||||||
|
} else {
|
||||||
|
return AV_LOG_INFO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Config::AudioBuffer() {
|
||||||
|
auto re = configs.find("audioBuffer");
|
||||||
|
if (re == configs.end()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return std::stoul((*re).second);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Config::VideoBuffer() {
|
||||||
|
auto re = configs.find("videoBuffer");
|
||||||
|
if (re == configs.end()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return std::stoul((*re).second);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
19
config.hpp
19
config.hpp
@@ -1,12 +1,31 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
#include <utility>
|
||||||
|
#include "patch_config.h"
|
||||||
|
|
||||||
class Config {
|
class Config {
|
||||||
public:
|
public:
|
||||||
std::unordered_map<std::string, std::string> configs;
|
std::unordered_map<std::string, std::string> configs;
|
||||||
|
#if HAVE_MPV
|
||||||
|
std::list<std::pair<std::string, std::string>> mpv;
|
||||||
|
#endif
|
||||||
Config() {
|
Config() {
|
||||||
configs["defaultFont"] = "微软雅黑";
|
configs["defaultFont"] = "微软雅黑";
|
||||||
configs["stringReplaceFile"] = "";
|
configs["stringReplaceFile"] = "";
|
||||||
|
#if HAVE_PLAYER
|
||||||
|
configs["appendLogging"] = "false";
|
||||||
|
configs["loggingFile"] = "";
|
||||||
|
configs["loggingLevel"] = "info";
|
||||||
|
configs["audioBuffer"] = "0";
|
||||||
|
configs["videoBuffer"] = "0";
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
bool Load(std::string path);
|
bool Load(std::string path);
|
||||||
|
#if HAVE_PLAYER
|
||||||
|
bool IsAppendLogging();
|
||||||
|
int LoggingLevel();
|
||||||
|
uint32_t AudioBuffer();
|
||||||
|
uint32_t VideoBuffer();
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|||||||
301
dllmain.cpp
301
dllmain.cpp
@@ -8,6 +8,13 @@
|
|||||||
#include "fileop.h"
|
#include "fileop.h"
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include "string_replace_file.hpp"
|
#include "string_replace_file.hpp"
|
||||||
|
#include "patch_config.h"
|
||||||
|
#if HAVE_PLAYER
|
||||||
|
#include "player.h"
|
||||||
|
#endif
|
||||||
|
#if HAVE_MPV
|
||||||
|
#include "mpv/client.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
static HFONT(WINAPI *TrueCreateFontW)(int nHeight, int nWidth, int nEscapement, int nOrientation, int fnWeight, DWORD dwItalic, DWORD dwUnderline, DWORD dwStrikeOut, DWORD dwCharSet, DWORD dwOutPrecision, DWORD dwClipPrecision, DWORD dwQuality, DWORD dwPitchAndFamily, LPCWSTR lpFaceName) = CreateFontW;
|
static HFONT(WINAPI *TrueCreateFontW)(int nHeight, int nWidth, int nEscapement, int nOrientation, int fnWeight, DWORD dwItalic, DWORD dwUnderline, DWORD dwStrikeOut, DWORD dwCharSet, DWORD dwOutPrecision, DWORD dwClipPrecision, DWORD dwQuality, DWORD dwPitchAndFamily, LPCWSTR lpFaceName) = CreateFontW;
|
||||||
static HFONT(WINAPI *TrueCreateFontA)(int nHeight, int nWidth, int nEscapement, int nOrientation, int fnWeight, DWORD dwItalic, DWORD dwUnderline, DWORD dwStrikeOut, DWORD dwCharSet, DWORD dwOutPrecision, DWORD dwClipPrecision, DWORD dwQuality, DWORD dwPitchAndFamily, LPCSTR lpFaceName) = CreateFontA;
|
static HFONT(WINAPI *TrueCreateFontA)(int nHeight, int nWidth, int nEscapement, int nOrientation, int fnWeight, DWORD dwItalic, DWORD dwUnderline, DWORD dwStrikeOut, DWORD dwCharSet, DWORD dwOutPrecision, DWORD dwClipPrecision, DWORD dwQuality, DWORD dwPitchAndFamily, LPCSTR lpFaceName) = CreateFontA;
|
||||||
@@ -17,11 +24,35 @@ static BOOL(WINAPI *TrueCloseHandle)(HANDLE hObject) = CloseHandle;
|
|||||||
static DWORD(WINAPI *TrueGetFileSize)(HANDLE hFile, LPDWORD lpFileSizeHigh) = GetFileSize;
|
static DWORD(WINAPI *TrueGetFileSize)(HANDLE hFile, LPDWORD lpFileSizeHigh) = GetFileSize;
|
||||||
static decltype(GetFileSizeEx) *TrueGetFileSizeEx = GetFileSizeEx;
|
static decltype(GetFileSizeEx) *TrueGetFileSizeEx = GetFileSizeEx;
|
||||||
static DWORD(WINAPI *TrueSetFilePointer)(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod) = SetFilePointer;
|
static DWORD(WINAPI *TrueSetFilePointer)(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod) = SetFilePointer;
|
||||||
|
static decltype(SetFilePointerEx) *TrueSetFilePointerEx = SetFilePointerEx;
|
||||||
|
static decltype(GetFileType) *TrueGetFileType = GetFileType;
|
||||||
|
static decltype(GetFileAttributesW) *TrueGetFileAttributesW = GetFileAttributesW;
|
||||||
|
static decltype(GetFileAttributesExW) *TrueGetFileAttributesExW = GetFileAttributesExW;
|
||||||
|
|
||||||
|
#if HAVE_PLAYER || HAVE_MPV
|
||||||
|
typedef int64_t(*OpenMediaFileAndGetDuration)(DWORD* duration, const char* arcName, const char* videoName);
|
||||||
|
typedef int64_t(*IsPlaying)();
|
||||||
|
typedef int64_t(*IsMediaPlaying)();
|
||||||
|
typedef int64_t(*ReleaseDirectShowGraph)();
|
||||||
|
#endif
|
||||||
|
|
||||||
static Config config;
|
static Config config;
|
||||||
static std::wstring defaultFont;
|
static std::wstring defaultFont;
|
||||||
static VFS vfs;
|
static VFS vfs;
|
||||||
static StringReplaceFile replaceFile;
|
static StringReplaceFile replaceFile;
|
||||||
|
#if HAVE_PLAYER
|
||||||
|
static PlayerSession* player = NULL;
|
||||||
|
static PlayerSettings* settings = NULL;
|
||||||
|
#endif
|
||||||
|
#if HAVE_MPV
|
||||||
|
static mpv_handle* player = NULL;
|
||||||
|
#endif
|
||||||
|
#if HAVE_PLAYER || HAVE_MPV
|
||||||
|
static OpenMediaFileAndGetDuration OpenMediaFileAndGetDurationFunc = NULL;
|
||||||
|
static IsPlaying IsPlayingFunc = NULL;
|
||||||
|
static IsMediaPlaying IsMediaPlayingFunc = NULL;
|
||||||
|
static ReleaseDirectShowGraph ReleaseDirectShowGraphFunc = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
char* to_utf8(char* target, const char* source, UINT cp) {
|
char* to_utf8(char* target, const char* source, UINT cp) {
|
||||||
int count = MultiByteToWideChar(cp, MB_ERR_INVALID_CHARS, source, -1, NULL, 0);
|
int count = MultiByteToWideChar(cp, MB_ERR_INVALID_CHARS, source, -1, NULL, 0);
|
||||||
@@ -69,8 +100,221 @@ PVOID GetHandle() {
|
|||||||
return (char*)hModule + 0xf40e0;
|
return (char*)hModule + 0xf40e0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if HAVE_PLAYER || HAVE_MPV
|
||||||
|
HWND* GetHwndPointer() {
|
||||||
|
HMODULE hModule = GetModuleHandleA(NULL);
|
||||||
|
return (HWND*)((char*)hModule + 0x1e1620);
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenMediaFileAndGetDuration GetOpenMediaFileAndGetDuration() {
|
||||||
|
HMODULE hModule = GetModuleHandleA(NULL);
|
||||||
|
return (OpenMediaFileAndGetDuration)((char*)hModule + 0xed3d0);
|
||||||
|
}
|
||||||
|
|
||||||
|
IsPlaying GetIsPlaying() {
|
||||||
|
HMODULE hModule = GetModuleHandleA(NULL);
|
||||||
|
return (IsPlaying)((char*)hModule + 0xed810);
|
||||||
|
}
|
||||||
|
|
||||||
|
IsMediaPlaying GetIsMediaPlaying() {
|
||||||
|
HMODULE hModule = GetModuleHandleA(NULL);
|
||||||
|
return (IsMediaPlaying)((char*)hModule + 0xed2f0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReleaseDirectShowGraph GetReleaseDirectShowGraph() {
|
||||||
|
HMODULE hModule = GetModuleHandleA(NULL);
|
||||||
|
return (ReleaseDirectShowGraph)((char*)hModule + 0xed610);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
static PVOID h = nullptr;
|
static PVOID h = nullptr;
|
||||||
|
|
||||||
|
#if HAVE_PLAYER || HAVE_MPV
|
||||||
|
|
||||||
|
int64_t HookedOpenMediaFileAndGetDuration(DWORD* duration, const char* arcName, const char* videoName) {
|
||||||
|
int64_t ok = 0;
|
||||||
|
#if HAVE_PLAYER
|
||||||
|
player_free(&player);
|
||||||
|
player_log(AV_LOG_INFO, "BGI: Open Video: %s, %s\n", arcName, videoName);
|
||||||
|
if (fileop::exists(videoName)) {
|
||||||
|
player_log(AV_LOG_INFO, "Video file exists: %s\n", videoName);
|
||||||
|
if (!settings) {
|
||||||
|
settings = player_settings_init();
|
||||||
|
if (!settings) {
|
||||||
|
player_log(AV_LOG_ERROR, "Failed to initialize player settings.\n");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
player_settings_set_resize(settings, 0);
|
||||||
|
uint32_t audioBuffer = config.AudioBuffer();
|
||||||
|
if (audioBuffer) {
|
||||||
|
player_settings_set_audio_buffer_size(settings, audioBuffer);
|
||||||
|
}
|
||||||
|
uint32_t videoBuffer = config.VideoBuffer();
|
||||||
|
if (videoBuffer) {
|
||||||
|
player_settings_set_video_buffer_size(settings, videoBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
player_settings_set_hWnd(settings, (void**)GetHwndPointer());
|
||||||
|
if (player_create2(videoName, &player, settings)) {
|
||||||
|
player_log(AV_LOG_ERROR, "Failed to create player session.\n");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
if (wait_player_inited(player)) {
|
||||||
|
player_log(AV_LOG_ERROR, "Failed to initialize player.\n");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
// 可能导致BGI关掉视频?
|
||||||
|
// player_wait_until_buffer_is_full(player);
|
||||||
|
if (player_play(player)) {
|
||||||
|
player_log(AV_LOG_ERROR, "Failed to play video.\n");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
int64_t dur;
|
||||||
|
if (!player_get_duration(player, &dur)) {
|
||||||
|
if (duration) {
|
||||||
|
*duration = dur / 1000;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
player_log(AV_LOG_WARNING, "Failed to get video duration.\n");
|
||||||
|
}
|
||||||
|
goto works;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if HAVE_MPV
|
||||||
|
if (player) {
|
||||||
|
mpv_terminate_destroy(player);
|
||||||
|
player = NULL;
|
||||||
|
}
|
||||||
|
if (fileop::exists(videoName)) {
|
||||||
|
player = mpv_create();
|
||||||
|
if (!player) {
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
int err = mpv_initialize(player);
|
||||||
|
if (err) {
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
HWND hwnd = *GetHwndPointer();
|
||||||
|
int64_t wid = (int64_t)(uint32_t)hwnd;
|
||||||
|
mpv_set_option(player, "wid", MPV_FORMAT_INT64, &wid);
|
||||||
|
mpv_set_option_string(player, "config", "no");
|
||||||
|
mpv_set_option_string(player, "input-default-bindings", "no");
|
||||||
|
mpv_set_option_string(player, "hwdec", "auto");
|
||||||
|
mpv_set_option_string(player, "auto-window-resize", "no");
|
||||||
|
for (auto& i : config.mpv) {
|
||||||
|
mpv_set_option_string(player, i.first.c_str(), i.second.c_str());
|
||||||
|
}
|
||||||
|
const char* cmd[] = { "loadfile", videoName, nullptr };
|
||||||
|
err = mpv_command(player, cmd);
|
||||||
|
if (err) {
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
while (1) {
|
||||||
|
mpv_event* event = mpv_wait_event(player, 10);
|
||||||
|
if (event->event_id == MPV_EVENT_FILE_LOADED) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (event->event_id == MPV_EVENT_SHUTDOWN) {
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const char* play_cmd[] = { "set", "pause", "no", NULL };
|
||||||
|
err = mpv_command(player, play_cmd);
|
||||||
|
if (err) {
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
while (1) {
|
||||||
|
int64_t idle = 0;
|
||||||
|
if (mpv_get_property(player, "core-idle", MPV_FORMAT_FLAG, &idle) < 0) {
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
if (!idle) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Sleep(10);
|
||||||
|
}
|
||||||
|
int64_t dur = 0;
|
||||||
|
mpv_get_property(player, "duration/full", MPV_FORMAT_INT64, &dur);
|
||||||
|
if (duration) {
|
||||||
|
*duration = dur;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
end:
|
||||||
|
ok = OpenMediaFileAndGetDurationFunc(duration, arcName, videoName);
|
||||||
|
works:
|
||||||
|
#if HAVE_PLAYER
|
||||||
|
if (duration) {
|
||||||
|
char tmp[32];
|
||||||
|
int64_t dur = *duration * 1000;
|
||||||
|
player_ts_make_string(tmp, dur);
|
||||||
|
player_log(AV_LOG_INFO, "Video duration: %s\n", tmp);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t HookedIsPlaying() {
|
||||||
|
#if HAVE_PLAYER
|
||||||
|
if (player) {
|
||||||
|
return player_is_playing(player);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if HAVE_MPV
|
||||||
|
if (player) {
|
||||||
|
int64_t re = 0;
|
||||||
|
mpv_get_property(player, "core-idle", MPV_FORMAT_FLAG, &re);
|
||||||
|
return !re;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return IsPlayingFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t HookedIsMediaPlaying() {
|
||||||
|
#if HAVE_PLAYER
|
||||||
|
if (player) {
|
||||||
|
int64_t re = player_is_playing(player);
|
||||||
|
// 释放播放器,BGI在播放完毕后不会手动释放
|
||||||
|
if (!re) {
|
||||||
|
player_log(AV_LOG_INFO, "Auto Close\n");
|
||||||
|
player_free(&player);
|
||||||
|
}
|
||||||
|
return re;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if HAVE_MPV
|
||||||
|
if (player) {
|
||||||
|
int64_t re = 0;
|
||||||
|
mpv_get_property(player, "core-idle", MPV_FORMAT_FLAG, &re);
|
||||||
|
if (re) {
|
||||||
|
mpv_terminate_destroy(player);
|
||||||
|
player = NULL;
|
||||||
|
}
|
||||||
|
return !re;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
int64_t re = IsMediaPlayingFunc();
|
||||||
|
return re;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t HookedReleaseDirectShowGraph() {
|
||||||
|
#if HAVE_PLAYER
|
||||||
|
player_log(AV_LOG_INFO, "BGI: Close\n");
|
||||||
|
player_free(&player);
|
||||||
|
#endif
|
||||||
|
#if HAVE_MPV
|
||||||
|
if (player) {
|
||||||
|
mpv_terminate_destroy(player);
|
||||||
|
player = NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return ReleaseDirectShowGraphFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
HFONT WINAPI HookedCreateFontW(int nHeight, int nWidth, int nEscapement, int nOrientation, int fnWeight, DWORD dwItalic, DWORD dwUnderline, DWORD dwStrikeOut, DWORD dwCharSet, DWORD dwOutPrecision, DWORD dwClipPrecision, DWORD dwQuality, DWORD dwPitchAndFamily, LPCWSTR lpFaceName) {
|
HFONT WINAPI HookedCreateFontW(int nHeight, int nWidth, int nEscapement, int nOrientation, int fnWeight, DWORD dwItalic, DWORD dwUnderline, DWORD dwStrikeOut, DWORD dwCharSet, DWORD dwOutPrecision, DWORD dwClipPrecision, DWORD dwQuality, DWORD dwPitchAndFamily, LPCWSTR lpFaceName) {
|
||||||
std::wstring name(lpFaceName);
|
std::wstring name(lpFaceName);
|
||||||
if (name == L"Meiryo") {
|
if (name == L"Meiryo") {
|
||||||
@@ -143,6 +387,34 @@ DWORD WINAPI HookedSetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDi
|
|||||||
return TrueSetFilePointer(hFile, lDistanceToMove, lpDistanceToMoveHigh, dwMoveMethod);
|
return TrueSetFilePointer(hFile, lDistanceToMove, lpDistanceToMoveHigh, dwMoveMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOL WINAPI HookedSetFilePointerEx(HANDLE hFile, LARGE_INTEGER liDistanceToMove, PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod) {
|
||||||
|
if (vfs.ContainsHandle(hFile)) {
|
||||||
|
return vfs.SetFilePointerEx(hFile, liDistanceToMove, lpNewFilePointer, dwMoveMethod);
|
||||||
|
}
|
||||||
|
return TrueSetFilePointerEx(hFile, liDistanceToMove, lpNewFilePointer, dwMoveMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD WINAPI HookedGetFileType(HANDLE hFile) {
|
||||||
|
if (vfs.ContainsHandle(hFile)) {
|
||||||
|
return FILE_TYPE_DISK;
|
||||||
|
}
|
||||||
|
return TrueGetFileType(hFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD WINAPI HookedGetFileAttributesW(LPCWSTR lpFileName) {
|
||||||
|
if (vfs.ContainsFile(lpFileName)) {
|
||||||
|
return FILE_ATTRIBUTE_READONLY;
|
||||||
|
}
|
||||||
|
return TrueGetFileAttributesW(lpFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL WINAPI HookedGetFileAttributesExW(LPCWSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId, LPVOID lpFileInformation) {
|
||||||
|
if (vfs.ContainsFile(lpFileName)) {
|
||||||
|
return vfs.GetFileAttributesExW(lpFileName, fInfoLevelId, lpFileInformation);
|
||||||
|
}
|
||||||
|
return TrueGetFileAttributesExW(lpFileName, fInfoLevelId, lpFileInformation);
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" __declspec(dllexport) void Attach() {
|
extern "C" __declspec(dllexport) void Attach() {
|
||||||
config.Load("config.txt");
|
config.Load("config.txt");
|
||||||
if (!wchar_util::str_to_wstr(defaultFont, config.configs["defaultFont"], CP_UTF8)) {
|
if (!wchar_util::str_to_wstr(defaultFont, config.configs["defaultFont"], CP_UTF8)) {
|
||||||
@@ -151,15 +423,28 @@ extern "C" __declspec(dllexport) void Attach() {
|
|||||||
if (defaultFont.empty()) {
|
if (defaultFont.empty()) {
|
||||||
defaultFont = L"微软雅黑";
|
defaultFont = L"微软雅黑";
|
||||||
}
|
}
|
||||||
if (!vfs.AddArchive("jewena-chs.dat")) {
|
#if HAVE_PLAYER
|
||||||
MessageBoxW(NULL, L"无法打开 jewena-chs.dat。请检查文件是否存在", L"错误", MB_ICONERROR);
|
auto loggingFile = config.configs["loggingFile"];
|
||||||
ExitProcess(1);
|
if (!loggingFile.empty()) {
|
||||||
return;
|
set_player_log_file(loggingFile.c_str(), config.IsAppendLogging() ? 1 : 0, config.LoggingLevel());
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
vfs.AddArchive("jewena-chs.dat");
|
||||||
|
vfs.AddArchive("video.dat");
|
||||||
DetourTransactionBegin();
|
DetourTransactionBegin();
|
||||||
DetourUpdateThread(GetCurrentThread());
|
DetourUpdateThread(GetCurrentThread());
|
||||||
h = GetHandle();
|
h = GetHandle();
|
||||||
DetourAttach(&h, (PVOID)jis_to_utf8);
|
DetourAttach(&h, (PVOID)jis_to_utf8);
|
||||||
|
#if HAVE_PLAYER || HAVE_MPV
|
||||||
|
OpenMediaFileAndGetDurationFunc = GetOpenMediaFileAndGetDuration();
|
||||||
|
DetourAttach(&OpenMediaFileAndGetDurationFunc, HookedOpenMediaFileAndGetDuration);
|
||||||
|
IsPlayingFunc = GetIsPlaying();
|
||||||
|
DetourAttach(&IsPlayingFunc, HookedIsPlaying);
|
||||||
|
IsMediaPlayingFunc = GetIsMediaPlaying();
|
||||||
|
DetourAttach(&IsMediaPlayingFunc, HookedIsMediaPlaying);
|
||||||
|
ReleaseDirectShowGraphFunc = GetReleaseDirectShowGraph();
|
||||||
|
DetourAttach(&ReleaseDirectShowGraphFunc, HookedReleaseDirectShowGraph);
|
||||||
|
#endif
|
||||||
DetourAttach(&TrueCreateFontW, HookedCreateFontW);
|
DetourAttach(&TrueCreateFontW, HookedCreateFontW);
|
||||||
DetourAttach(&TrueCreateFontA, HookedCreateFontA);
|
DetourAttach(&TrueCreateFontA, HookedCreateFontA);
|
||||||
DetourAttach(&TrueCreateFileW, HookedCreateFileW);
|
DetourAttach(&TrueCreateFileW, HookedCreateFileW);
|
||||||
@@ -168,6 +453,10 @@ extern "C" __declspec(dllexport) void Attach() {
|
|||||||
DetourAttach(&TrueGetFileSize, HookedGetFileSize);
|
DetourAttach(&TrueGetFileSize, HookedGetFileSize);
|
||||||
DetourAttach(&TrueGetFileSizeEx, HookedGetFileSizeEx);
|
DetourAttach(&TrueGetFileSizeEx, HookedGetFileSizeEx);
|
||||||
DetourAttach(&TrueSetFilePointer, HookedSetFilePointer);
|
DetourAttach(&TrueSetFilePointer, HookedSetFilePointer);
|
||||||
|
DetourAttach(&TrueSetFilePointerEx, HookedSetFilePointerEx);
|
||||||
|
DetourAttach(&TrueGetFileType, HookedGetFileType);
|
||||||
|
DetourAttach(&TrueGetFileAttributesW, HookedGetFileAttributesW);
|
||||||
|
DetourAttach(&TrueGetFileAttributesExW, HookedGetFileAttributesExW);
|
||||||
DetourTransactionCommit();
|
DetourTransactionCommit();
|
||||||
std::string stringReplaceFile = config.configs["stringReplaceFile"];
|
std::string stringReplaceFile = config.configs["stringReplaceFile"];
|
||||||
if (!stringReplaceFile.empty()) {
|
if (!stringReplaceFile.empty()) {
|
||||||
@@ -194,6 +483,10 @@ extern "C" __declspec(dllexport) void Detach() {
|
|||||||
DetourDetach(&TrueGetFileSize, HookedGetFileSize);
|
DetourDetach(&TrueGetFileSize, HookedGetFileSize);
|
||||||
DetourDetach(&TrueGetFileSizeEx, HookedGetFileSizeEx);
|
DetourDetach(&TrueGetFileSizeEx, HookedGetFileSizeEx);
|
||||||
DetourDetach(&TrueSetFilePointer, HookedSetFilePointer);
|
DetourDetach(&TrueSetFilePointer, HookedSetFilePointer);
|
||||||
|
DetourDetach(&TrueSetFilePointerEx, HookedSetFilePointerEx);
|
||||||
|
DetourDetach(&TrueGetFileType, HookedGetFileType);
|
||||||
|
DetourDetach(&TrueGetFileAttributesW, HookedGetFileAttributesW);
|
||||||
|
DetourDetach(&TrueGetFileAttributesExW, HookedGetFileAttributesExW);
|
||||||
DetourTransactionCommit();
|
DetourTransactionCommit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2032
include/mpv/client.h
Normal file
2032
include/mpv/client.h
Normal file
File diff suppressed because it is too large
Load Diff
759
include/mpv/render.h
Normal file
759
include/mpv/render.h
Normal file
@@ -0,0 +1,759 @@
|
|||||||
|
/* Copyright (C) 2018 the mpv developers
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MPV_CLIENT_API_RENDER_H_
|
||||||
|
#define MPV_CLIENT_API_RENDER_H_
|
||||||
|
|
||||||
|
#include "client.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overview
|
||||||
|
* --------
|
||||||
|
*
|
||||||
|
* This API can be used to make mpv render using supported graphic APIs (such
|
||||||
|
* as OpenGL). It can be used to handle video display.
|
||||||
|
*
|
||||||
|
* The renderer needs to be created with mpv_render_context_create() before
|
||||||
|
* you start playback (or otherwise cause a VO to be created). Then (with most
|
||||||
|
* backends) mpv_render_context_render() can be used to explicitly render the
|
||||||
|
* current video frame. Use mpv_render_context_set_update_callback() to get
|
||||||
|
* notified when there is a new frame to draw.
|
||||||
|
*
|
||||||
|
* Preferably rendering should be done in a separate thread. If you call
|
||||||
|
* normal libmpv API functions on the renderer thread, deadlocks can result
|
||||||
|
* (these are made non-fatal with timeouts, but user experience will obviously
|
||||||
|
* suffer). See "Threading" section below.
|
||||||
|
*
|
||||||
|
* You can output and embed video without this API by setting the mpv "wid"
|
||||||
|
* option to a native window handle (see "Embedding the video window" section
|
||||||
|
* in the client.h header). In general, using the render API is recommended,
|
||||||
|
* because window embedding can cause various issues, especially with GUI
|
||||||
|
* toolkits and certain platforms.
|
||||||
|
*
|
||||||
|
* Supported backends
|
||||||
|
* ------------------
|
||||||
|
*
|
||||||
|
* OpenGL: via MPV_RENDER_API_TYPE_OPENGL, see render_gl.h header.
|
||||||
|
* Software: via MPV_RENDER_API_TYPE_SW, see section "Software renderer"
|
||||||
|
*
|
||||||
|
* Threading
|
||||||
|
* ---------
|
||||||
|
*
|
||||||
|
* You are recommended to do rendering on a separate thread than normal libmpv
|
||||||
|
* use.
|
||||||
|
*
|
||||||
|
* The mpv_render_* functions can be called from any thread, under the
|
||||||
|
* following conditions:
|
||||||
|
* - only one of the mpv_render_* functions can be called at the same time
|
||||||
|
* (unless they belong to different mpv cores created by mpv_create())
|
||||||
|
* - never can be called from within the callbacks set with
|
||||||
|
* mpv_set_wakeup_callback() or mpv_render_context_set_update_callback()
|
||||||
|
* - if the OpenGL backend is used, for all functions the OpenGL context
|
||||||
|
* must be "current" in the calling thread, and it must be the same OpenGL
|
||||||
|
* context as the mpv_render_context was created with. Otherwise, undefined
|
||||||
|
* behavior will occur.
|
||||||
|
* - the thread does not call libmpv API functions other than the mpv_render_*
|
||||||
|
* functions, except APIs which are declared as safe (see below). Likewise,
|
||||||
|
* there must be no lock or wait dependency from the render thread to a
|
||||||
|
* thread using other libmpv functions. Basically, the situation that your
|
||||||
|
* render thread waits for a "not safe" libmpv API function to return must
|
||||||
|
* not happen. If you ignore this requirement, deadlocks can happen, which
|
||||||
|
* are made non-fatal with timeouts; then playback quality will be degraded,
|
||||||
|
* and the message
|
||||||
|
* mpv_render_context_render() not being called or stuck.
|
||||||
|
* is logged. If you set MPV_RENDER_PARAM_ADVANCED_CONTROL, you promise that
|
||||||
|
* this won't happen, and must absolutely guarantee it, or a real deadlock
|
||||||
|
* will freeze the mpv core thread forever.
|
||||||
|
*
|
||||||
|
* libmpv functions which are safe to call from a render thread are:
|
||||||
|
* - functions marked with "Safe to be called from mpv render API threads."
|
||||||
|
* - client.h functions which don't have an explicit or implicit mpv_handle
|
||||||
|
* parameter
|
||||||
|
* - mpv_render_* functions; but only for the same mpv_render_context pointer.
|
||||||
|
* If the pointer is different, mpv_render_context_free() is not safe. (The
|
||||||
|
* reason is that if MPV_RENDER_PARAM_ADVANCED_CONTROL is set, it may have
|
||||||
|
* to process still queued requests from the core, which it can do only for
|
||||||
|
* the current context, while requests for other contexts would deadlock.
|
||||||
|
* Also, it may have to wait and block for the core to terminate the video
|
||||||
|
* chain to make sure no resources are used after context destruction.)
|
||||||
|
* - if the mpv_handle parameter refers to a different mpv core than the one
|
||||||
|
* you're rendering for (very obscure, but allowed)
|
||||||
|
*
|
||||||
|
* Note about old libmpv version:
|
||||||
|
*
|
||||||
|
* Before API version 1.105 (basically in mpv 0.29.x), simply enabling
|
||||||
|
* MPV_RENDER_PARAM_ADVANCED_CONTROL could cause deadlock issues. This can
|
||||||
|
* be worked around by setting the "vd-lavc-dr" option to "no".
|
||||||
|
* In addition, you were required to call all mpv_render*() API functions
|
||||||
|
* from the same thread on which mpv_render_context_create() was originally
|
||||||
|
* run (for the same the mpv_render_context). Not honoring it led to UB
|
||||||
|
* (deadlocks, use of invalid mp_thread handles), even if you moved your GL
|
||||||
|
* context to a different thread correctly.
|
||||||
|
* These problems were addressed in API version 1.105 (mpv 0.30.0).
|
||||||
|
*
|
||||||
|
* Context and handle lifecycle
|
||||||
|
* ----------------------------
|
||||||
|
*
|
||||||
|
* Video initialization will fail if the render context was not initialized yet
|
||||||
|
* (with mpv_render_context_create()), or it will revert to a VO that creates
|
||||||
|
* its own window.
|
||||||
|
*
|
||||||
|
* Currently, there can be only 1 mpv_render_context at a time per mpv core.
|
||||||
|
*
|
||||||
|
* Calling mpv_render_context_free() while a VO is using the render context is
|
||||||
|
* active will disable video.
|
||||||
|
*
|
||||||
|
* You must free the context with mpv_render_context_free() before the mpv core
|
||||||
|
* is destroyed. If this doesn't happen, undefined behavior will result.
|
||||||
|
*
|
||||||
|
* Software renderer
|
||||||
|
* -----------------
|
||||||
|
*
|
||||||
|
* MPV_RENDER_API_TYPE_SW provides an extremely simple (but slow) renderer to
|
||||||
|
* memory surfaces. You probably don't want to use this. Use other render API
|
||||||
|
* types, or other methods of video embedding.
|
||||||
|
*
|
||||||
|
* Use mpv_render_context_create() with MPV_RENDER_PARAM_API_TYPE set to
|
||||||
|
* MPV_RENDER_API_TYPE_SW.
|
||||||
|
*
|
||||||
|
* Call mpv_render_context_render() with various MPV_RENDER_PARAM_SW_* fields
|
||||||
|
* to render the video frame to an in-memory surface. The following fields are
|
||||||
|
* required: MPV_RENDER_PARAM_SW_SIZE, MPV_RENDER_PARAM_SW_FORMAT,
|
||||||
|
* MPV_RENDER_PARAM_SW_STRIDE, MPV_RENDER_PARAM_SW_POINTER.
|
||||||
|
*
|
||||||
|
* This method of rendering is very slow, because everything, including color
|
||||||
|
* conversion, scaling, and OSD rendering, is done on the CPU, single-threaded.
|
||||||
|
* In particular, large video or display sizes, as well as presence of OSD or
|
||||||
|
* subtitles can make it too slow for realtime. As with other software rendering
|
||||||
|
* VOs, setting "sw-fast" may help. Enabling or disabling zimg may help,
|
||||||
|
* depending on the platform.
|
||||||
|
*
|
||||||
|
* In addition, certain multimedia job creation measures like HDR may not work
|
||||||
|
* properly, and will have to be manually handled by for example inserting
|
||||||
|
* filters.
|
||||||
|
*
|
||||||
|
* This API is not really suitable to extract individual frames from video etc.
|
||||||
|
* (basically non-playback uses) - there are better libraries for this. It can
|
||||||
|
* be used this way, but it may be clunky and tricky.
|
||||||
|
*
|
||||||
|
* Further notes:
|
||||||
|
* - MPV_RENDER_PARAM_FLIP_Y is currently ignored (unsupported)
|
||||||
|
* - MPV_RENDER_PARAM_DEPTH is ignored (meaningless)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opaque context, returned by mpv_render_context_create().
|
||||||
|
*/
|
||||||
|
typedef struct mpv_render_context mpv_render_context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parameters for mpv_render_param (which is used in a few places such as
|
||||||
|
* mpv_render_context_create().
|
||||||
|
*
|
||||||
|
* Also see mpv_render_param for conventions and how to use it.
|
||||||
|
*/
|
||||||
|
typedef enum mpv_render_param_type {
|
||||||
|
/**
|
||||||
|
* Not a valid value, but also used to terminate a params array. Its value
|
||||||
|
* is always guaranteed to be 0 (even if the ABI changes in the future).
|
||||||
|
*/
|
||||||
|
MPV_RENDER_PARAM_INVALID = 0,
|
||||||
|
/**
|
||||||
|
* The render API to use. Valid for mpv_render_context_create().
|
||||||
|
*
|
||||||
|
* Type: char*
|
||||||
|
*
|
||||||
|
* Defined APIs:
|
||||||
|
*
|
||||||
|
* MPV_RENDER_API_TYPE_OPENGL:
|
||||||
|
* OpenGL desktop 2.1 or later (preferably core profile compatible to
|
||||||
|
* OpenGL 3.2), or OpenGLES 2.0 or later.
|
||||||
|
* Providing MPV_RENDER_PARAM_OPENGL_INIT_PARAMS is required.
|
||||||
|
* It is expected that an OpenGL context is valid and "current" when
|
||||||
|
* calling mpv_render_* functions (unless specified otherwise). It
|
||||||
|
* must be the same context for the same mpv_render_context.
|
||||||
|
*/
|
||||||
|
MPV_RENDER_PARAM_API_TYPE = 1,
|
||||||
|
/**
|
||||||
|
* Required parameters for initializing the OpenGL renderer. Valid for
|
||||||
|
* mpv_render_context_create().
|
||||||
|
* Type: mpv_opengl_init_params*
|
||||||
|
*/
|
||||||
|
MPV_RENDER_PARAM_OPENGL_INIT_PARAMS = 2,
|
||||||
|
/**
|
||||||
|
* Describes a GL render target. Valid for mpv_render_context_render().
|
||||||
|
* Type: mpv_opengl_fbo*
|
||||||
|
*/
|
||||||
|
MPV_RENDER_PARAM_OPENGL_FBO = 3,
|
||||||
|
/**
|
||||||
|
* Control flipped rendering. Valid for mpv_render_context_render().
|
||||||
|
* Type: int*
|
||||||
|
* If the value is set to 0, render normally. Otherwise, render it flipped,
|
||||||
|
* which is needed e.g. when rendering to an OpenGL default framebuffer
|
||||||
|
* (which has a flipped coordinate system).
|
||||||
|
*/
|
||||||
|
MPV_RENDER_PARAM_FLIP_Y = 4,
|
||||||
|
/**
|
||||||
|
* Control surface depth. Valid for mpv_render_context_render().
|
||||||
|
* Type: int*
|
||||||
|
* This implies the depth of the surface passed to the render function in
|
||||||
|
* bits per channel. If omitted or set to 0, the renderer will assume 8.
|
||||||
|
* Typically used to control dithering.
|
||||||
|
*/
|
||||||
|
MPV_RENDER_PARAM_DEPTH = 5,
|
||||||
|
/**
|
||||||
|
* ICC profile blob. Valid for mpv_render_context_set_parameter().
|
||||||
|
* Type: mpv_byte_array*
|
||||||
|
* Set an ICC profile for use with the "icc-profile-auto" option. (If the
|
||||||
|
* option is not enabled, the ICC data will not be used.)
|
||||||
|
*/
|
||||||
|
MPV_RENDER_PARAM_ICC_PROFILE = 6,
|
||||||
|
/**
|
||||||
|
* Ambient light in lux. Valid for mpv_render_context_set_parameter().
|
||||||
|
* Type: int*
|
||||||
|
* This can be used for automatic gamma correction.
|
||||||
|
*/
|
||||||
|
MPV_RENDER_PARAM_AMBIENT_LIGHT = 7,
|
||||||
|
/**
|
||||||
|
* X11 Display, sometimes used for hwdec. Valid for
|
||||||
|
* mpv_render_context_create(). The Display must stay valid for the lifetime
|
||||||
|
* of the mpv_render_context.
|
||||||
|
* Type: Display*
|
||||||
|
*/
|
||||||
|
MPV_RENDER_PARAM_X11_DISPLAY = 8,
|
||||||
|
/**
|
||||||
|
* Wayland display, sometimes used for hwdec. Valid for
|
||||||
|
* mpv_render_context_create(). The wl_display must stay valid for the
|
||||||
|
* lifetime of the mpv_render_context.
|
||||||
|
* Type: struct wl_display*
|
||||||
|
*/
|
||||||
|
MPV_RENDER_PARAM_WL_DISPLAY = 9,
|
||||||
|
/**
|
||||||
|
* Better control about rendering and enabling some advanced features. Valid
|
||||||
|
* for mpv_render_context_create().
|
||||||
|
*
|
||||||
|
* This conflates multiple requirements the API user promises to abide if
|
||||||
|
* this option is enabled:
|
||||||
|
*
|
||||||
|
* - The API user's render thread, which is calling the mpv_render_*()
|
||||||
|
* functions, never waits for the core. Otherwise deadlocks can happen.
|
||||||
|
* See "Threading" section.
|
||||||
|
* - The callback set with mpv_render_context_set_update_callback() can now
|
||||||
|
* be called even if there is no new frame. The API user should call the
|
||||||
|
* mpv_render_context_update() function, and interpret the return value
|
||||||
|
* for whether a new frame should be rendered.
|
||||||
|
* - Correct functionality is impossible if the update callback is not set,
|
||||||
|
* or not set soon enough after mpv_render_context_create() (the core can
|
||||||
|
* block while waiting for you to call mpv_render_context_update(), and
|
||||||
|
* if the update callback is not correctly set, it will deadlock, or
|
||||||
|
* block for too long).
|
||||||
|
*
|
||||||
|
* In general, setting this option will enable the following features (and
|
||||||
|
* possibly more):
|
||||||
|
*
|
||||||
|
* - "Direct rendering", which means the player decodes directly to a
|
||||||
|
* texture, which saves a copy per video frame ("vd-lavc-dr" option
|
||||||
|
* needs to be enabled, and the rendering backend as well as the
|
||||||
|
* underlying GPU API/driver needs to have support for it).
|
||||||
|
* - Rendering screenshots with the GPU API if supported by the backend
|
||||||
|
* (instead of using a suboptimal software fallback via libswscale).
|
||||||
|
*
|
||||||
|
* Warning: do not just add this without reading the "Threading" section
|
||||||
|
* above, and then wondering that deadlocks happen. The
|
||||||
|
* requirements are tricky. But also note that even if advanced
|
||||||
|
* control is disabled, not adhering to the rules will lead to
|
||||||
|
* playback problems. Enabling advanced controls simply makes
|
||||||
|
* violating these rules fatal.
|
||||||
|
*
|
||||||
|
* Type: int*: 0 for disable (default), 1 for enable
|
||||||
|
*/
|
||||||
|
MPV_RENDER_PARAM_ADVANCED_CONTROL = 10,
|
||||||
|
/**
|
||||||
|
* Return information about the next frame to render. Valid for
|
||||||
|
* mpv_render_context_get_info().
|
||||||
|
*
|
||||||
|
* Type: mpv_render_frame_info*
|
||||||
|
*
|
||||||
|
* It strictly returns information about the _next_ frame. The implication
|
||||||
|
* is that e.g. mpv_render_context_update()'s return value will have
|
||||||
|
* MPV_RENDER_UPDATE_FRAME set, and the user is supposed to call
|
||||||
|
* mpv_render_context_render(). If there is no next frame, then the
|
||||||
|
* return value will have is_valid set to 0.
|
||||||
|
*/
|
||||||
|
MPV_RENDER_PARAM_NEXT_FRAME_INFO = 11,
|
||||||
|
/**
|
||||||
|
* Enable or disable video timing. Valid for mpv_render_context_render().
|
||||||
|
*
|
||||||
|
* Type: int*: 0 for disable, 1 for enable (default)
|
||||||
|
*
|
||||||
|
* When video is timed to audio, the player attempts to render video a bit
|
||||||
|
* ahead, and then do a blocking wait until the target display time is
|
||||||
|
* reached. This blocks mpv_render_context_render() for up to the amount
|
||||||
|
* specified with the "video-timing-offset" global option. You can set
|
||||||
|
* this parameter to 0 to disable this kind of waiting. If you do, it's
|
||||||
|
* recommended to use the target time value in mpv_render_frame_info to
|
||||||
|
* wait yourself, or to set the "video-timing-offset" to 0 instead.
|
||||||
|
*
|
||||||
|
* Disabling this without doing anything in addition will result in A/V sync
|
||||||
|
* being slightly off.
|
||||||
|
*/
|
||||||
|
MPV_RENDER_PARAM_BLOCK_FOR_TARGET_TIME = 12,
|
||||||
|
/**
|
||||||
|
* Use to skip rendering in mpv_render_context_render().
|
||||||
|
*
|
||||||
|
* Type: int*: 0 for rendering (default), 1 for skipping
|
||||||
|
*
|
||||||
|
* If this is set, you don't need to pass a target surface to the render
|
||||||
|
* function (and if you do, it's completely ignored). This can still call
|
||||||
|
* into the lower level APIs (i.e. if you use OpenGL, the OpenGL context
|
||||||
|
* must be set).
|
||||||
|
*
|
||||||
|
* Be aware that the render API will consider this frame as having been
|
||||||
|
* rendered. All other normal rules also apply, for example about whether
|
||||||
|
* you have to call mpv_render_context_report_swap(). It also does timing
|
||||||
|
* in the same way.
|
||||||
|
*/
|
||||||
|
MPV_RENDER_PARAM_SKIP_RENDERING = 13,
|
||||||
|
/**
|
||||||
|
* Deprecated. Not supported. Use MPV_RENDER_PARAM_DRM_DISPLAY_V2 instead.
|
||||||
|
* Type : struct mpv_opengl_drm_params*
|
||||||
|
*/
|
||||||
|
MPV_RENDER_PARAM_DRM_DISPLAY = 14,
|
||||||
|
/**
|
||||||
|
* DRM draw surface size, contains draw surface dimensions.
|
||||||
|
* Valid for mpv_render_context_create().
|
||||||
|
* Type : struct mpv_opengl_drm_draw_surface_size*
|
||||||
|
*/
|
||||||
|
MPV_RENDER_PARAM_DRM_DRAW_SURFACE_SIZE = 15,
|
||||||
|
/**
|
||||||
|
* DRM display, contains drm display handles.
|
||||||
|
* Valid for mpv_render_context_create().
|
||||||
|
* Type : struct mpv_opengl_drm_params_v2*
|
||||||
|
*/
|
||||||
|
MPV_RENDER_PARAM_DRM_DISPLAY_V2 = 16,
|
||||||
|
/**
|
||||||
|
* MPV_RENDER_API_TYPE_SW only: rendering target surface size, mandatory.
|
||||||
|
* Valid for MPV_RENDER_API_TYPE_SW & mpv_render_context_render().
|
||||||
|
* Type: int[2] (e.g.: int s[2] = {w, h}; param.data = &s[0];)
|
||||||
|
*
|
||||||
|
* The video frame is transformed as with other VOs. Typically, this means
|
||||||
|
* the video gets scaled and black bars are added if the video size or
|
||||||
|
* aspect ratio mismatches with the target size.
|
||||||
|
*/
|
||||||
|
MPV_RENDER_PARAM_SW_SIZE = 17,
|
||||||
|
/**
|
||||||
|
* MPV_RENDER_API_TYPE_SW only: rendering target surface pixel format,
|
||||||
|
* mandatory.
|
||||||
|
* Valid for MPV_RENDER_API_TYPE_SW & mpv_render_context_render().
|
||||||
|
* Type: char* (e.g.: char *f = "rgb0"; param.data = f;)
|
||||||
|
*
|
||||||
|
* Valid values are:
|
||||||
|
* "rgb0", "bgr0", "0bgr", "0rgb"
|
||||||
|
* 4 bytes per pixel RGB, 1 byte (8 bit) per component, component bytes
|
||||||
|
* with increasing address from left to right (e.g. "rgb0" has r at
|
||||||
|
* address 0), the "0" component contains uninitialized garbage (often
|
||||||
|
* the value 0, but not necessarily; the bad naming is inherited from
|
||||||
|
* FFmpeg)
|
||||||
|
* Pixel alignment size: 4 bytes
|
||||||
|
* "rgb24"
|
||||||
|
* 3 bytes per pixel RGB. This is strongly discouraged because it is
|
||||||
|
* very slow.
|
||||||
|
* Pixel alignment size: 1 bytes
|
||||||
|
* other
|
||||||
|
* The API may accept other pixel formats, using mpv internal format
|
||||||
|
* names, as long as it's internally marked as RGB, has exactly 1
|
||||||
|
* plane, and is supported as conversion output. It is not a good idea
|
||||||
|
* to rely on any of these. Their semantics and handling could change.
|
||||||
|
*/
|
||||||
|
MPV_RENDER_PARAM_SW_FORMAT = 18,
|
||||||
|
/**
|
||||||
|
* MPV_RENDER_API_TYPE_SW only: rendering target surface bytes per line,
|
||||||
|
* mandatory.
|
||||||
|
* Valid for MPV_RENDER_API_TYPE_SW & mpv_render_context_render().
|
||||||
|
* Type: size_t*
|
||||||
|
*
|
||||||
|
* This is the number of bytes between a pixel (x, y) and (x, y + 1) on the
|
||||||
|
* target surface. It must be a multiple of the pixel size, and have space
|
||||||
|
* for the surface width as specified by MPV_RENDER_PARAM_SW_SIZE.
|
||||||
|
*
|
||||||
|
* Both stride and pointer value should be a multiple of 64 to facilitate
|
||||||
|
* fast SIMD operation. Lower alignment might trigger slower code paths,
|
||||||
|
* and in the worst case, will copy the entire target frame. If mpv is built
|
||||||
|
* with zimg (and zimg is not disabled), the performance impact might be
|
||||||
|
* less.
|
||||||
|
* In either cases, the pointer and stride must be aligned at least to the
|
||||||
|
* pixel alignment size. Otherwise, crashes and undefined behavior is
|
||||||
|
* possible on platforms which do not support unaligned accesses (either
|
||||||
|
* through normal memory access or aligned SIMD memory access instructions).
|
||||||
|
*/
|
||||||
|
MPV_RENDER_PARAM_SW_STRIDE = 19,
|
||||||
|
/*
|
||||||
|
* MPV_RENDER_API_TYPE_SW only: rendering target surface pixel data pointer,
|
||||||
|
* mandatory.
|
||||||
|
* Valid for MPV_RENDER_API_TYPE_SW & mpv_render_context_render().
|
||||||
|
* Type: void*
|
||||||
|
*
|
||||||
|
* This points to the first pixel at the left/top corner (0, 0). In
|
||||||
|
* particular, each line y starts at (pointer + stride * y). Upon rendering,
|
||||||
|
* all data between pointer and (pointer + stride * h) is overwritten.
|
||||||
|
* Whether the padding between (w, y) and (0, y + 1) is overwritten is left
|
||||||
|
* unspecified (it should not be, but unfortunately some scaler backends
|
||||||
|
* will do it anyway). It is assumed that even the padding after the last
|
||||||
|
* line (starting at bytepos(w, h) until (pointer + stride * h)) is
|
||||||
|
* writable.
|
||||||
|
*
|
||||||
|
* See MPV_RENDER_PARAM_SW_STRIDE for alignment requirements.
|
||||||
|
*/
|
||||||
|
MPV_RENDER_PARAM_SW_POINTER = 20,
|
||||||
|
} mpv_render_param_type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For backwards compatibility with the old naming of
|
||||||
|
* MPV_RENDER_PARAM_DRM_DRAW_SURFACE_SIZE
|
||||||
|
*/
|
||||||
|
#define MPV_RENDER_PARAM_DRM_OSD_SIZE MPV_RENDER_PARAM_DRM_DRAW_SURFACE_SIZE
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to pass arbitrary parameters to some mpv_render_* functions. The
|
||||||
|
* meaning of the data parameter is determined by the type, and each
|
||||||
|
* MPV_RENDER_PARAM_* documents what type the value must point to.
|
||||||
|
*
|
||||||
|
* Each value documents the required data type as the pointer you cast to
|
||||||
|
* void* and set on mpv_render_param.data. For example, if MPV_RENDER_PARAM_FOO
|
||||||
|
* documents the type as Something* , then the code should look like this:
|
||||||
|
*
|
||||||
|
* Something foo = {...};
|
||||||
|
* mpv_render_param param;
|
||||||
|
* param.type = MPV_RENDER_PARAM_FOO;
|
||||||
|
* param.data = & foo;
|
||||||
|
*
|
||||||
|
* Normally, the data field points to exactly 1 object. If the type is char*,
|
||||||
|
* it points to a 0-terminated string.
|
||||||
|
*
|
||||||
|
* In all cases (unless documented otherwise) the pointers need to remain
|
||||||
|
* valid during the call only. Unless otherwise documented, the API functions
|
||||||
|
* will not write to the params array or any data pointed to it.
|
||||||
|
*
|
||||||
|
* As a convention, parameter arrays are always terminated by type==0. There
|
||||||
|
* is no specific order of the parameters required. The order of the 2 fields in
|
||||||
|
* this struct is guaranteed (even after ABI changes).
|
||||||
|
*/
|
||||||
|
typedef struct mpv_render_param {
|
||||||
|
enum mpv_render_param_type type;
|
||||||
|
void *data;
|
||||||
|
} mpv_render_param;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Predefined values for MPV_RENDER_PARAM_API_TYPE.
|
||||||
|
*/
|
||||||
|
// See render_gl.h
|
||||||
|
#define MPV_RENDER_API_TYPE_OPENGL "opengl"
|
||||||
|
// See section "Software renderer"
|
||||||
|
#define MPV_RENDER_API_TYPE_SW "sw"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flags used in mpv_render_frame_info.flags. Each value represents a bit in it.
|
||||||
|
*/
|
||||||
|
typedef enum mpv_render_frame_info_flag {
|
||||||
|
/**
|
||||||
|
* Set if there is actually a next frame. If unset, there is no next frame
|
||||||
|
* yet, and other flags and fields that require a frame to be queued will
|
||||||
|
* be unset.
|
||||||
|
*
|
||||||
|
* This is set for _any_ kind of frame, even for redraw requests.
|
||||||
|
*
|
||||||
|
* Note that when this is unset, it simply means no new frame was
|
||||||
|
* decoded/queued yet, not necessarily that the end of the video was
|
||||||
|
* reached. A new frame can be queued after some time.
|
||||||
|
*
|
||||||
|
* If the return value of mpv_render_context_render() had the
|
||||||
|
* MPV_RENDER_UPDATE_FRAME flag set, this flag will usually be set as well,
|
||||||
|
* unless the frame is rendered, or discarded by other asynchronous events.
|
||||||
|
*/
|
||||||
|
MPV_RENDER_FRAME_INFO_PRESENT = 1 << 0,
|
||||||
|
/**
|
||||||
|
* If set, the frame is not an actual new video frame, but a redraw request.
|
||||||
|
* For example if the video is paused, and an option that affects video
|
||||||
|
* rendering was changed (or any other reason), an update request can be
|
||||||
|
* issued and this flag will be set.
|
||||||
|
*
|
||||||
|
* Typically, redraw frames will not be subject to video timing.
|
||||||
|
*
|
||||||
|
* Implies MPV_RENDER_FRAME_INFO_PRESENT.
|
||||||
|
*/
|
||||||
|
MPV_RENDER_FRAME_INFO_REDRAW = 1 << 1,
|
||||||
|
/**
|
||||||
|
* If set, this is supposed to reproduce the previous frame perfectly. This
|
||||||
|
* is usually used for certain "video-sync" options ("display-..." modes).
|
||||||
|
* Typically the renderer will blit the video from a FBO. Unset otherwise.
|
||||||
|
*
|
||||||
|
* Implies MPV_RENDER_FRAME_INFO_PRESENT.
|
||||||
|
*/
|
||||||
|
MPV_RENDER_FRAME_INFO_REPEAT = 1 << 2,
|
||||||
|
/**
|
||||||
|
* If set, the player timing code expects that the user thread blocks on
|
||||||
|
* vsync (by either delaying the render call, or by making a call to
|
||||||
|
* mpv_render_context_report_swap() at vsync time).
|
||||||
|
*
|
||||||
|
* Implies MPV_RENDER_FRAME_INFO_PRESENT.
|
||||||
|
*/
|
||||||
|
MPV_RENDER_FRAME_INFO_BLOCK_VSYNC = 1 << 3,
|
||||||
|
} mpv_render_frame_info_flag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information about the next video frame that will be rendered. Can be
|
||||||
|
* retrieved with MPV_RENDER_PARAM_NEXT_FRAME_INFO.
|
||||||
|
*/
|
||||||
|
typedef struct mpv_render_frame_info {
|
||||||
|
/**
|
||||||
|
* A bitset of mpv_render_frame_info_flag values (i.e. multiple flags are
|
||||||
|
* combined with bitwise or).
|
||||||
|
*/
|
||||||
|
uint64_t flags;
|
||||||
|
/**
|
||||||
|
* Absolute time at which the frame is supposed to be displayed. This is in
|
||||||
|
* the same unit and base as the time returned by mpv_get_time_us(). For
|
||||||
|
* frames that are redrawn, or if vsync locked video timing is used (see
|
||||||
|
* "video-sync" option), then this can be 0. The "video-timing-offset"
|
||||||
|
* option determines how much "headroom" the render thread gets (but a high
|
||||||
|
* enough frame rate can reduce it anyway). mpv_render_context_render() will
|
||||||
|
* normally block until the time is elapsed, unless you pass it
|
||||||
|
* MPV_RENDER_PARAM_BLOCK_FOR_TARGET_TIME = 0.
|
||||||
|
*/
|
||||||
|
int64_t target_time;
|
||||||
|
} mpv_render_frame_info;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the renderer state. Depending on the backend used, this will
|
||||||
|
* access the underlying GPU API and initialize its own objects.
|
||||||
|
*
|
||||||
|
* You must free the context with mpv_render_context_free(). Not doing so before
|
||||||
|
* the mpv core is destroyed may result in memory leaks or crashes.
|
||||||
|
*
|
||||||
|
* Currently, only at most 1 context can exists per mpv core (it represents the
|
||||||
|
* main video output).
|
||||||
|
*
|
||||||
|
* You should pass the following parameters:
|
||||||
|
* - MPV_RENDER_PARAM_API_TYPE to select the underlying backend/GPU API.
|
||||||
|
* - Backend-specific init parameter, like MPV_RENDER_PARAM_OPENGL_INIT_PARAMS.
|
||||||
|
* - Setting MPV_RENDER_PARAM_ADVANCED_CONTROL and following its rules is
|
||||||
|
* strongly recommended.
|
||||||
|
* - If you want to use hwdec, possibly hwdec interop resources.
|
||||||
|
*
|
||||||
|
* @param res set to the context (on success) or NULL (on failure). The value
|
||||||
|
* is never read and always overwritten.
|
||||||
|
* @param mpv handle used to get the core (the mpv_render_context won't depend
|
||||||
|
* on this specific handle, only the core referenced by it)
|
||||||
|
* @param params an array of parameters, terminated by type==0. It's left
|
||||||
|
* unspecified what happens with unknown parameters. At least
|
||||||
|
* MPV_RENDER_PARAM_API_TYPE is required, and most backends will
|
||||||
|
* require another backend-specific parameter.
|
||||||
|
* @return error code, including but not limited to:
|
||||||
|
* MPV_ERROR_UNSUPPORTED: the OpenGL version is not supported
|
||||||
|
* (or required extensions are missing)
|
||||||
|
* MPV_ERROR_NOT_IMPLEMENTED: an unknown API type was provided, or
|
||||||
|
* support for the requested API was not
|
||||||
|
* built in the used libmpv binary.
|
||||||
|
* MPV_ERROR_INVALID_PARAMETER: at least one of the provided parameters was
|
||||||
|
* not valid.
|
||||||
|
*/
|
||||||
|
MPV_EXPORT int mpv_render_context_create(mpv_render_context **res, mpv_handle *mpv,
|
||||||
|
mpv_render_param *params);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to change a single parameter. Not all backends and parameter types
|
||||||
|
* support all kinds of changes.
|
||||||
|
*
|
||||||
|
* @param ctx a valid render context
|
||||||
|
* @param param the parameter type and data that should be set
|
||||||
|
* @return error code. If a parameter could actually be changed, this returns
|
||||||
|
* success, otherwise an error code depending on the parameter type
|
||||||
|
* and situation.
|
||||||
|
*/
|
||||||
|
MPV_EXPORT int mpv_render_context_set_parameter(mpv_render_context *ctx,
|
||||||
|
mpv_render_param param);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve information from the render context. This is NOT a counterpart to
|
||||||
|
* mpv_render_context_set_parameter(), because you generally can't read
|
||||||
|
* parameters set with it, and this function is not meant for this purpose.
|
||||||
|
* Instead, this is for communicating information from the renderer back to the
|
||||||
|
* user. See mpv_render_param_type; entries which support this function
|
||||||
|
* explicitly mention it, and for other entries you can assume it will fail.
|
||||||
|
*
|
||||||
|
* You pass param with param.type set and param.data pointing to a variable
|
||||||
|
* of the required data type. The function will then overwrite that variable
|
||||||
|
* with the returned value (at least on success).
|
||||||
|
*
|
||||||
|
* @param ctx a valid render context
|
||||||
|
* @param param the parameter type and data that should be retrieved
|
||||||
|
* @return error code. If a parameter could actually be retrieved, this returns
|
||||||
|
* success, otherwise an error code depending on the parameter type
|
||||||
|
* and situation. MPV_ERROR_NOT_IMPLEMENTED is used for unknown
|
||||||
|
* param.type, or if retrieving it is not supported.
|
||||||
|
*/
|
||||||
|
MPV_EXPORT int mpv_render_context_get_info(mpv_render_context *ctx,
|
||||||
|
mpv_render_param param);
|
||||||
|
|
||||||
|
typedef void (*mpv_render_update_fn)(void *cb_ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the callback that notifies you when a new video frame is available, or
|
||||||
|
* if the video display configuration somehow changed and requires a redraw.
|
||||||
|
* Similar to mpv_set_wakeup_callback(), you must not call any mpv API from
|
||||||
|
* the callback, and all the other listed restrictions apply (such as not
|
||||||
|
* exiting the callback by throwing exceptions).
|
||||||
|
*
|
||||||
|
* This can be called from any thread, except from an update callback. In case
|
||||||
|
* of the OpenGL backend, no OpenGL state or API is accessed.
|
||||||
|
*
|
||||||
|
* Calling this will raise an update callback immediately.
|
||||||
|
*
|
||||||
|
* @param callback callback(callback_ctx) is called if the frame should be
|
||||||
|
* redrawn
|
||||||
|
* @param callback_ctx opaque argument to the callback
|
||||||
|
*/
|
||||||
|
MPV_EXPORT void mpv_render_context_set_update_callback(mpv_render_context *ctx,
|
||||||
|
mpv_render_update_fn callback,
|
||||||
|
void *callback_ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The API user is supposed to call this when the update callback was invoked
|
||||||
|
* (like all mpv_render_* functions, this has to happen on the render thread,
|
||||||
|
* and _not_ from the update callback itself).
|
||||||
|
*
|
||||||
|
* This is optional if MPV_RENDER_PARAM_ADVANCED_CONTROL was not set (default).
|
||||||
|
* Otherwise, it's a hard requirement that this is called after each update
|
||||||
|
* callback. If multiple update callback happened, and the function could not
|
||||||
|
* be called sooner, it's OK to call it once after the last callback.
|
||||||
|
*
|
||||||
|
* If an update callback happens during or after this function, the function
|
||||||
|
* must be called again at the soonest possible time.
|
||||||
|
*
|
||||||
|
* If MPV_RENDER_PARAM_ADVANCED_CONTROL was set, this will do additional work
|
||||||
|
* such as allocating textures for the video decoder.
|
||||||
|
*
|
||||||
|
* @return a bitset of mpv_render_update_flag values (i.e. multiple flags are
|
||||||
|
* combined with bitwise or). Typically, this will tell the API user
|
||||||
|
* what should happen next. E.g. if the MPV_RENDER_UPDATE_FRAME flag is
|
||||||
|
* set, mpv_render_context_render() should be called. If flags unknown
|
||||||
|
* to the API user are set, or if the return value is 0, nothing needs
|
||||||
|
* to be done.
|
||||||
|
*/
|
||||||
|
MPV_EXPORT uint64_t mpv_render_context_update(mpv_render_context *ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flags returned by mpv_render_context_update(). Each value represents a bit
|
||||||
|
* in the function's return value.
|
||||||
|
*/
|
||||||
|
typedef enum mpv_render_update_flag {
|
||||||
|
/**
|
||||||
|
* A new video frame must be rendered. mpv_render_context_render() must be
|
||||||
|
* called.
|
||||||
|
*/
|
||||||
|
MPV_RENDER_UPDATE_FRAME = 1 << 0,
|
||||||
|
} mpv_render_context_flag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render video.
|
||||||
|
*
|
||||||
|
* Typically renders the video to a target surface provided via mpv_render_param
|
||||||
|
* (the details depend on the backend in use). Options like "panscan" are
|
||||||
|
* applied to determine which part of the video should be visible and how the
|
||||||
|
* video should be scaled. You can change these options at runtime by using the
|
||||||
|
* mpv property API.
|
||||||
|
*
|
||||||
|
* The renderer will reconfigure itself every time the target surface
|
||||||
|
* configuration (such as size) is changed.
|
||||||
|
*
|
||||||
|
* This function implicitly pulls a video frame from the internal queue and
|
||||||
|
* renders it. If no new frame is available, the previous frame is redrawn.
|
||||||
|
* The update callback set with mpv_render_context_set_update_callback()
|
||||||
|
* notifies you when a new frame was added. The details potentially depend on
|
||||||
|
* the backends and the provided parameters.
|
||||||
|
*
|
||||||
|
* Generally, libmpv will invoke your update callback some time before the video
|
||||||
|
* frame should be shown, and then lets this function block until the supposed
|
||||||
|
* display time. This will limit your rendering to video FPS. You can prevent
|
||||||
|
* this by setting the "video-timing-offset" global option to 0. (This applies
|
||||||
|
* only to "audio" video sync mode.)
|
||||||
|
*
|
||||||
|
* You should pass the following parameters:
|
||||||
|
* - Backend-specific target object, such as MPV_RENDER_PARAM_OPENGL_FBO.
|
||||||
|
* - Possibly transformations, such as MPV_RENDER_PARAM_FLIP_Y.
|
||||||
|
*
|
||||||
|
* @param ctx a valid render context
|
||||||
|
* @param params an array of parameters, terminated by type==0. Which parameters
|
||||||
|
* are required depends on the backend. It's left unspecified what
|
||||||
|
* happens with unknown parameters.
|
||||||
|
* @return error code
|
||||||
|
*/
|
||||||
|
MPV_EXPORT int mpv_render_context_render(mpv_render_context *ctx, mpv_render_param *params);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tell the renderer that a frame was flipped at the given time. This is
|
||||||
|
* optional, but can help the player to achieve better timing.
|
||||||
|
*
|
||||||
|
* Note that calling this at least once informs libmpv that you will use this
|
||||||
|
* function. If you use it inconsistently, expect bad video playback.
|
||||||
|
*
|
||||||
|
* If this is called while no video is initialized, it is ignored.
|
||||||
|
*
|
||||||
|
* @param ctx a valid render context
|
||||||
|
*/
|
||||||
|
MPV_EXPORT void mpv_render_context_report_swap(mpv_render_context *ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy the mpv renderer state.
|
||||||
|
*
|
||||||
|
* If video is still active (e.g. a file playing), video will be disabled
|
||||||
|
* forcefully.
|
||||||
|
*
|
||||||
|
* @param ctx a valid render context. After this function returns, this is not
|
||||||
|
* a valid pointer anymore. NULL is also allowed and does nothing.
|
||||||
|
*/
|
||||||
|
MPV_EXPORT void mpv_render_context_free(mpv_render_context *ctx);
|
||||||
|
|
||||||
|
#ifdef MPV_CPLUGIN_DYNAMIC_SYM
|
||||||
|
|
||||||
|
MPV_DEFINE_SYM_PTR(mpv_render_context_create)
|
||||||
|
#define mpv_render_context_create pfn_mpv_render_context_create
|
||||||
|
MPV_DEFINE_SYM_PTR(mpv_render_context_set_parameter)
|
||||||
|
#define mpv_render_context_set_parameter pfn_mpv_render_context_set_parameter
|
||||||
|
MPV_DEFINE_SYM_PTR(mpv_render_context_get_info)
|
||||||
|
#define mpv_render_context_get_info pfn_mpv_render_context_get_info
|
||||||
|
MPV_DEFINE_SYM_PTR(mpv_render_context_set_update_callback)
|
||||||
|
#define mpv_render_context_set_update_callback pfn_mpv_render_context_set_update_callback
|
||||||
|
MPV_DEFINE_SYM_PTR(mpv_render_context_update)
|
||||||
|
#define mpv_render_context_update pfn_mpv_render_context_update
|
||||||
|
MPV_DEFINE_SYM_PTR(mpv_render_context_render)
|
||||||
|
#define mpv_render_context_render pfn_mpv_render_context_render
|
||||||
|
MPV_DEFINE_SYM_PTR(mpv_render_context_report_swap)
|
||||||
|
#define mpv_render_context_report_swap pfn_mpv_render_context_report_swap
|
||||||
|
MPV_DEFINE_SYM_PTR(mpv_render_context_free)
|
||||||
|
#define mpv_render_context_free pfn_mpv_render_context_free
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
211
include/mpv/render_gl.h
Normal file
211
include/mpv/render_gl.h
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
/* Copyright (C) 2018 the mpv developers
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MPV_CLIENT_API_RENDER_GL_H_
|
||||||
|
#define MPV_CLIENT_API_RENDER_GL_H_
|
||||||
|
|
||||||
|
#include "render.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OpenGL backend
|
||||||
|
* --------------
|
||||||
|
*
|
||||||
|
* This header contains definitions for using OpenGL with the render.h API.
|
||||||
|
*
|
||||||
|
* OpenGL interop
|
||||||
|
* --------------
|
||||||
|
*
|
||||||
|
* The OpenGL backend has some special rules, because OpenGL itself uses
|
||||||
|
* implicit per-thread contexts, which causes additional API problems.
|
||||||
|
*
|
||||||
|
* This assumes the OpenGL context lives on a certain thread controlled by the
|
||||||
|
* API user. All mpv_render_* APIs have to be assumed to implicitly use the
|
||||||
|
* OpenGL context if you pass a mpv_render_context using the OpenGL backend,
|
||||||
|
* unless specified otherwise.
|
||||||
|
*
|
||||||
|
* The OpenGL context is indirectly accessed through the OpenGL function
|
||||||
|
* pointers returned by the get_proc_address callback in mpv_opengl_init_params.
|
||||||
|
* Generally, mpv will not load the system OpenGL library when using this API.
|
||||||
|
*
|
||||||
|
* OpenGL state
|
||||||
|
* ------------
|
||||||
|
*
|
||||||
|
* OpenGL has a large amount of implicit state. All the mpv functions mentioned
|
||||||
|
* above expect that the OpenGL state is reasonably set to OpenGL standard
|
||||||
|
* defaults. Likewise, mpv will attempt to leave the OpenGL context with
|
||||||
|
* standard defaults. The following state is excluded from this:
|
||||||
|
*
|
||||||
|
* - the glViewport state
|
||||||
|
* - the glScissor state (but GL_SCISSOR_TEST is in its default value)
|
||||||
|
* - glBlendFuncSeparate() state (but GL_BLEND is in its default value)
|
||||||
|
* - glClearColor() state
|
||||||
|
* - mpv may overwrite the callback set with glDebugMessageCallback()
|
||||||
|
* - mpv always disables GL_DITHER at init
|
||||||
|
*
|
||||||
|
* Messing with the state could be avoided by creating shared OpenGL contexts,
|
||||||
|
* but this is avoided for the sake of compatibility and interoperability.
|
||||||
|
*
|
||||||
|
* On OpenGL 2.1, mpv will strictly call functions like glGenTextures() to
|
||||||
|
* create OpenGL objects. You will have to do the same. This ensures that
|
||||||
|
* objects created by mpv and the API users don't clash. Also, legacy state
|
||||||
|
* must be either in its defaults, or not interfere with core state.
|
||||||
|
*
|
||||||
|
* API use
|
||||||
|
* -------
|
||||||
|
*
|
||||||
|
* The mpv_render_* API is used. That API supports multiple backends, and this
|
||||||
|
* section documents specifics for the OpenGL backend.
|
||||||
|
*
|
||||||
|
* Use mpv_render_context_create() with MPV_RENDER_PARAM_API_TYPE set to
|
||||||
|
* MPV_RENDER_API_TYPE_OPENGL, and MPV_RENDER_PARAM_OPENGL_INIT_PARAMS provided.
|
||||||
|
*
|
||||||
|
* Call mpv_render_context_render() with MPV_RENDER_PARAM_OPENGL_FBO to render
|
||||||
|
* the video frame to an FBO.
|
||||||
|
*
|
||||||
|
* Hardware decoding
|
||||||
|
* -----------------
|
||||||
|
*
|
||||||
|
* Hardware decoding via this API is fully supported, but requires some
|
||||||
|
* additional setup. (At least if direct hardware decoding modes are wanted,
|
||||||
|
* instead of copying back surface data from GPU to CPU RAM.)
|
||||||
|
*
|
||||||
|
* There may be certain requirements on the OpenGL implementation:
|
||||||
|
*
|
||||||
|
* - Windows: ANGLE is required (although in theory GL/DX interop could be used)
|
||||||
|
* - Intel/Linux: EGL is required, and also the native display resource needs
|
||||||
|
* to be provided (e.g. MPV_RENDER_PARAM_X11_DISPLAY for X11 and
|
||||||
|
* MPV_RENDER_PARAM_WL_DISPLAY for Wayland)
|
||||||
|
* - nVidia/Linux: Both GLX and EGL should work (GLX is required if vdpau is
|
||||||
|
* used, e.g. due to old drivers.)
|
||||||
|
* - macOS: CGL is required (CGLGetCurrentContext() returning non-NULL)
|
||||||
|
* - iOS: EAGL is required (EAGLContext.currentContext returning non-nil)
|
||||||
|
*
|
||||||
|
* Once these things are setup, hardware decoding can be enabled/disabled at
|
||||||
|
* any time by setting the "hwdec" property.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For initializing the mpv OpenGL state via MPV_RENDER_PARAM_OPENGL_INIT_PARAMS.
|
||||||
|
*/
|
||||||
|
typedef struct mpv_opengl_init_params {
|
||||||
|
/**
|
||||||
|
* This retrieves OpenGL function pointers, and will use them in subsequent
|
||||||
|
* operation.
|
||||||
|
* Usually, you can simply call the GL context APIs from this callback (e.g.
|
||||||
|
* glXGetProcAddressARB or wglGetProcAddress), but some APIs do not always
|
||||||
|
* return pointers for all standard functions (even if present); in this
|
||||||
|
* case you have to compensate by looking up these functions yourself when
|
||||||
|
* libmpv wants to resolve them through this callback.
|
||||||
|
* libmpv will not normally attempt to resolve GL functions on its own, nor
|
||||||
|
* does it link to GL libraries directly.
|
||||||
|
*/
|
||||||
|
void *(*get_proc_address)(void *ctx, const char *name);
|
||||||
|
/**
|
||||||
|
* Value passed as ctx parameter to get_proc_address().
|
||||||
|
*/
|
||||||
|
void *get_proc_address_ctx;
|
||||||
|
} mpv_opengl_init_params;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For MPV_RENDER_PARAM_OPENGL_FBO.
|
||||||
|
*/
|
||||||
|
typedef struct mpv_opengl_fbo {
|
||||||
|
/**
|
||||||
|
* Framebuffer object name. This must be either a valid FBO generated by
|
||||||
|
* glGenFramebuffers() that is complete and color-renderable, or 0. If the
|
||||||
|
* value is 0, this refers to the OpenGL default framebuffer.
|
||||||
|
*/
|
||||||
|
int fbo;
|
||||||
|
/**
|
||||||
|
* Valid dimensions. This must refer to the size of the framebuffer. This
|
||||||
|
* must always be set.
|
||||||
|
*/
|
||||||
|
int w, h;
|
||||||
|
/**
|
||||||
|
* Underlying texture internal format (e.g. GL_RGBA8), or 0 if unknown. If
|
||||||
|
* this is the default framebuffer, this can be an equivalent.
|
||||||
|
*/
|
||||||
|
int internal_format;
|
||||||
|
} mpv_opengl_fbo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deprecated. For MPV_RENDER_PARAM_DRM_DISPLAY.
|
||||||
|
*/
|
||||||
|
typedef struct mpv_opengl_drm_params {
|
||||||
|
int fd;
|
||||||
|
int crtc_id;
|
||||||
|
int connector_id;
|
||||||
|
struct _drmModeAtomicReq **atomic_request_ptr;
|
||||||
|
int render_fd;
|
||||||
|
} mpv_opengl_drm_params;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For MPV_RENDER_PARAM_DRM_DRAW_SURFACE_SIZE.
|
||||||
|
*/
|
||||||
|
typedef struct mpv_opengl_drm_draw_surface_size {
|
||||||
|
/**
|
||||||
|
* size of the draw plane surface in pixels.
|
||||||
|
*/
|
||||||
|
int width, height;
|
||||||
|
} mpv_opengl_drm_draw_surface_size;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For MPV_RENDER_PARAM_DRM_DISPLAY_V2.
|
||||||
|
*/
|
||||||
|
typedef struct mpv_opengl_drm_params_v2 {
|
||||||
|
/**
|
||||||
|
* DRM fd (int). Set to -1 if invalid.
|
||||||
|
*/
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Currently used crtc id
|
||||||
|
*/
|
||||||
|
int crtc_id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Currently used connector id
|
||||||
|
*/
|
||||||
|
int connector_id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pointer to a drmModeAtomicReq pointer that is being used for the renderloop.
|
||||||
|
* This pointer should hold a pointer to the atomic request pointer
|
||||||
|
* The atomic request pointer is usually changed at every renderloop.
|
||||||
|
*/
|
||||||
|
struct _drmModeAtomicReq **atomic_request_ptr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DRM render node. Used for VAAPI interop.
|
||||||
|
* Set to -1 if invalid.
|
||||||
|
*/
|
||||||
|
int render_fd;
|
||||||
|
} mpv_opengl_drm_params_v2;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For backwards compatibility with the old naming of mpv_opengl_drm_draw_surface_size
|
||||||
|
*/
|
||||||
|
#define mpv_opengl_drm_osd_size mpv_opengl_drm_draw_surface_size
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
247
include/mpv/stream_cb.h
Normal file
247
include/mpv/stream_cb.h
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
/* Copyright (C) 2017 the mpv developers
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MPV_CLIENT_API_STREAM_CB_H_
|
||||||
|
#define MPV_CLIENT_API_STREAM_CB_H_
|
||||||
|
|
||||||
|
#include "client.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Warning: this API is not stable yet.
|
||||||
|
*
|
||||||
|
* Overview
|
||||||
|
* --------
|
||||||
|
*
|
||||||
|
* This API can be used to make mpv read from a stream with a custom
|
||||||
|
* implementation. This interface is inspired by funopen on BSD and
|
||||||
|
* fopencookie on linux. The stream is backed by user-defined callbacks
|
||||||
|
* which can implement customized open, read, seek, size and close behaviors.
|
||||||
|
*
|
||||||
|
* Usage
|
||||||
|
* -----
|
||||||
|
*
|
||||||
|
* Register your stream callbacks with the mpv_stream_cb_add_ro() function. You
|
||||||
|
* have to provide a mpv_stream_cb_open_ro_fn callback to it (open_fn argument).
|
||||||
|
*
|
||||||
|
* Once registered, you can `loadfile myprotocol://myfile`. Your open_fn will be
|
||||||
|
* invoked with the URI and you must fill out the provided mpv_stream_cb_info
|
||||||
|
* struct. This includes your stream callbacks (like read_fn), and an opaque
|
||||||
|
* cookie, which will be passed as the first argument to all the remaining
|
||||||
|
* stream callbacks.
|
||||||
|
*
|
||||||
|
* Note that your custom callbacks must not invoke libmpv APIs as that would
|
||||||
|
* cause a deadlock. (Unless you call a different mpv_handle than the one the
|
||||||
|
* callback was registered for, and the mpv_handles refer to different mpv
|
||||||
|
* instances.)
|
||||||
|
*
|
||||||
|
* Stream lifetime
|
||||||
|
* ---------------
|
||||||
|
*
|
||||||
|
* A stream remains valid until its close callback has been called. It's up to
|
||||||
|
* libmpv to call the close callback, and the libmpv user cannot close it
|
||||||
|
* directly with the stream_cb API.
|
||||||
|
*
|
||||||
|
* For example, if you consider your custom stream to become suddenly invalid
|
||||||
|
* (maybe because the underlying stream died), libmpv will continue using your
|
||||||
|
* stream. All you can do is returning errors from each callback, until libmpv
|
||||||
|
* gives up and closes it.
|
||||||
|
*
|
||||||
|
* Protocol registration and lifetime
|
||||||
|
* ----------------------------------
|
||||||
|
*
|
||||||
|
* Protocols remain registered until the mpv instance is terminated. This means
|
||||||
|
* in particular that it can outlive the mpv_handle that was used to register
|
||||||
|
* it, but once mpv_terminate_destroy() is called, your registered callbacks
|
||||||
|
* will not be called again.
|
||||||
|
*
|
||||||
|
* Protocol unregistration is finished after the mpv core has been destroyed
|
||||||
|
* (e.g. after mpv_terminate_destroy() has returned).
|
||||||
|
*
|
||||||
|
* If you do not call mpv_terminate_destroy() yourself (e.g. plugin-style code),
|
||||||
|
* you will have to deal with the registration or even streams outliving your
|
||||||
|
* code. Here are some possible ways to do this:
|
||||||
|
* - call mpv_terminate_destroy(), which destroys the core, and will make sure
|
||||||
|
* all streams are closed once this function returns
|
||||||
|
* - you refcount all resources your stream "cookies" reference, so that it
|
||||||
|
* doesn't matter if streams live longer than expected
|
||||||
|
* - create "cancellation" semantics: after your protocol has been unregistered,
|
||||||
|
* notify all your streams that are still opened, and make them drop all
|
||||||
|
* referenced resources - then return errors from the stream callbacks as
|
||||||
|
* long as the stream is still opened
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read callback used to implement a custom stream. The semantics of the
|
||||||
|
* callback match read(2) in blocking mode. Short reads are allowed (you can
|
||||||
|
* return less bytes than requested, and libmpv will retry reading the rest
|
||||||
|
* with another call). If no data can be immediately read, the callback must
|
||||||
|
* block until there is new data. A return of 0 will be interpreted as final
|
||||||
|
* EOF, although libmpv might retry the read, or seek to a different position.
|
||||||
|
*
|
||||||
|
* @param cookie opaque cookie identifying the stream,
|
||||||
|
* returned from mpv_stream_cb_open_fn
|
||||||
|
* @param buf buffer to read data into
|
||||||
|
* @param size of the buffer
|
||||||
|
* @return number of bytes read into the buffer
|
||||||
|
* @return 0 on EOF
|
||||||
|
* @return -1 on error
|
||||||
|
*/
|
||||||
|
typedef int64_t (*mpv_stream_cb_read_fn)(void *cookie, char *buf, uint64_t nbytes);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Seek callback used to implement a custom stream.
|
||||||
|
*
|
||||||
|
* Note that mpv will issue a seek to position 0 immediately after opening. This
|
||||||
|
* is used to test whether the stream is seekable (since seekability might
|
||||||
|
* depend on the URI contents, not just the protocol). Return
|
||||||
|
* MPV_ERROR_UNSUPPORTED if seeking is not implemented for this stream. This
|
||||||
|
* seek also serves to establish the fact that streams start at position 0.
|
||||||
|
*
|
||||||
|
* This callback can be NULL, in which it behaves as if always returning
|
||||||
|
* MPV_ERROR_UNSUPPORTED.
|
||||||
|
*
|
||||||
|
* @param cookie opaque cookie identifying the stream,
|
||||||
|
* returned from mpv_stream_cb_open_fn
|
||||||
|
* @param offset target absolute stream position
|
||||||
|
* @return the resulting offset of the stream
|
||||||
|
* MPV_ERROR_UNSUPPORTED or MPV_ERROR_GENERIC if the seek failed
|
||||||
|
*/
|
||||||
|
typedef int64_t (*mpv_stream_cb_seek_fn)(void *cookie, int64_t offset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Size callback used to implement a custom stream.
|
||||||
|
*
|
||||||
|
* Return MPV_ERROR_UNSUPPORTED if no size is known.
|
||||||
|
*
|
||||||
|
* This callback can be NULL, in which it behaves as if always returning
|
||||||
|
* MPV_ERROR_UNSUPPORTED.
|
||||||
|
*
|
||||||
|
* @param cookie opaque cookie identifying the stream,
|
||||||
|
* returned from mpv_stream_cb_open_fn
|
||||||
|
* @return the total size in bytes of the stream
|
||||||
|
*/
|
||||||
|
typedef int64_t (*mpv_stream_cb_size_fn)(void *cookie);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close callback used to implement a custom stream.
|
||||||
|
*
|
||||||
|
* @param cookie opaque cookie identifying the stream,
|
||||||
|
* returned from mpv_stream_cb_open_fn
|
||||||
|
*/
|
||||||
|
typedef void (*mpv_stream_cb_close_fn)(void *cookie);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel callback used to implement a custom stream.
|
||||||
|
*
|
||||||
|
* This callback is used to interrupt any current or future read and seek
|
||||||
|
* operations. It will be called from a separate thread than the demux
|
||||||
|
* thread, and should not block.
|
||||||
|
*
|
||||||
|
* This callback can be NULL.
|
||||||
|
*
|
||||||
|
* Available since API 1.106.
|
||||||
|
*
|
||||||
|
* @param cookie opaque cookie identifying the stream,
|
||||||
|
* returned from mpv_stream_cb_open_fn
|
||||||
|
*/
|
||||||
|
typedef void (*mpv_stream_cb_cancel_fn)(void *cookie);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See mpv_stream_cb_open_ro_fn callback.
|
||||||
|
*/
|
||||||
|
typedef struct mpv_stream_cb_info {
|
||||||
|
/**
|
||||||
|
* Opaque user-provided value, which will be passed to the other callbacks.
|
||||||
|
* The close callback will be called to release the cookie. It is not
|
||||||
|
* interpreted by mpv. It doesn't even need to be a valid pointer.
|
||||||
|
*
|
||||||
|
* The user sets this in the mpv_stream_cb_open_ro_fn callback.
|
||||||
|
*/
|
||||||
|
void *cookie;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callbacks set by the user in the mpv_stream_cb_open_ro_fn callback. Some
|
||||||
|
* of them are optional, and can be left unset.
|
||||||
|
*
|
||||||
|
* The following callbacks are mandatory: read_fn, close_fn
|
||||||
|
*/
|
||||||
|
mpv_stream_cb_read_fn read_fn;
|
||||||
|
mpv_stream_cb_seek_fn seek_fn;
|
||||||
|
mpv_stream_cb_size_fn size_fn;
|
||||||
|
mpv_stream_cb_close_fn close_fn;
|
||||||
|
mpv_stream_cb_cancel_fn cancel_fn; /* since API 1.106 */
|
||||||
|
} mpv_stream_cb_info;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open callback used to implement a custom read-only (ro) stream. The user
|
||||||
|
* must set the callback fields in the passed info struct. The cookie field
|
||||||
|
* also can be set to store state associated to the stream instance.
|
||||||
|
*
|
||||||
|
* Note that the info struct is valid only for the duration of this callback.
|
||||||
|
* You can't change the callbacks or the pointer to the cookie at a later point.
|
||||||
|
*
|
||||||
|
* Each stream instance created by the open callback can have different
|
||||||
|
* callbacks.
|
||||||
|
*
|
||||||
|
* The close_fn callback will terminate the stream instance. The pointers to
|
||||||
|
* your callbacks and cookie will be discarded, and the callbacks will not be
|
||||||
|
* called again.
|
||||||
|
*
|
||||||
|
* @param user_data opaque user data provided via mpv_stream_cb_add()
|
||||||
|
* @param uri name of the stream to be opened (with protocol prefix)
|
||||||
|
* @param info fields which the user should fill
|
||||||
|
* @return 0 on success, MPV_ERROR_LOADING_FAILED if the URI cannot be opened.
|
||||||
|
*/
|
||||||
|
typedef int (*mpv_stream_cb_open_ro_fn)(void *user_data, char *uri,
|
||||||
|
mpv_stream_cb_info *info);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a custom stream protocol. This will register a protocol handler under
|
||||||
|
* the given protocol prefix, and invoke the given callbacks if an URI with the
|
||||||
|
* matching protocol prefix is opened.
|
||||||
|
*
|
||||||
|
* The "ro" is for read-only - only read-only streams can be registered with
|
||||||
|
* this function.
|
||||||
|
*
|
||||||
|
* The callback remains registered until the mpv core is registered.
|
||||||
|
*
|
||||||
|
* If a custom stream with the same name is already registered, then the
|
||||||
|
* MPV_ERROR_INVALID_PARAMETER error is returned.
|
||||||
|
*
|
||||||
|
* @param protocol protocol prefix, for example "foo" for "foo://" URIs
|
||||||
|
* @param user_data opaque pointer passed into the mpv_stream_cb_open_fn
|
||||||
|
* callback.
|
||||||
|
* @return error code
|
||||||
|
*/
|
||||||
|
MPV_EXPORT int mpv_stream_cb_add_ro(mpv_handle *ctx, const char *protocol, void *user_data,
|
||||||
|
mpv_stream_cb_open_ro_fn open_fn);
|
||||||
|
|
||||||
|
#ifdef MPV_CPLUGIN_DYNAMIC_SYM
|
||||||
|
|
||||||
|
MPV_DEFINE_SYM_PTR(mpv_stream_cb_add_ro)
|
||||||
|
#define mpv_stream_cb_add_ro pfn_mpv_stream_cb_add_ro
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
193
include/player.h
Normal file
193
include/player.h
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
#ifndef _PLAYER_H
|
||||||
|
#define _PLAYER_H
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
#if BUILD_PLAYER
|
||||||
|
#define PLAYER_API __declspec(dllexport)
|
||||||
|
#else
|
||||||
|
#define PLAYER_API __declspec(dllimport)
|
||||||
|
#endif
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
#define PLAYER_API __attribute__((visibility("default")))
|
||||||
|
#else
|
||||||
|
#define PLAYER_API
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct PlayerSession PlayerSession;
|
||||||
|
typedef struct PlayerSettings PlayerSettings;
|
||||||
|
|
||||||
|
#ifndef BUILD_PLAYER
|
||||||
|
#define AV_LOG_QUIET -8
|
||||||
|
#define AV_LOG_PANIC 0
|
||||||
|
#define AV_LOG_FATAL 8
|
||||||
|
#define AV_LOG_ERROR 16
|
||||||
|
#define AV_LOG_WARNING 24
|
||||||
|
#define AV_LOG_INFO 32
|
||||||
|
#define AV_LOG_VERBOSE 40
|
||||||
|
#define AV_LOG_DEBUG 48
|
||||||
|
#define AV_LOG_TRACE 56
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define PLAYER_ERR_OK 0
|
||||||
|
#define PLAYER_ERR_NULLPTR 1
|
||||||
|
#define PLAYER_ERR_SDL 2
|
||||||
|
#define PLAYER_ERR_OOM 3
|
||||||
|
#define PLAYER_ERR_NO_STREAM_OR_DECODER 4
|
||||||
|
#define PLAYER_ERR_UNKNOWN_AUDIO_SAMPLE_FMT 5
|
||||||
|
#define PLAYER_ERR_FAILED_CREATE_MUTEX 6
|
||||||
|
#define PLAYER_ERR_WAIT_MUTEX_FAILED 7
|
||||||
|
#define PLAYER_ERR_FAILED_CREATE_THREAD 8
|
||||||
|
#define PLAYER_ERR_NO_DURATION 9
|
||||||
|
|
||||||
|
PLAYER_API const char* player_version_str();
|
||||||
|
PLAYER_API int32_t player_version();
|
||||||
|
/**
|
||||||
|
* @brief 设置播放器日志文件
|
||||||
|
*
|
||||||
|
* 如果之前设置过日志文件,会关闭之前的日志文件。
|
||||||
|
* @param filename 日志文件路径,如果为NULL则关闭之前的设置的日志文件
|
||||||
|
* @param append 是否追加到文件末尾
|
||||||
|
* @param max_level 最大日志级别,小于等于这个级别的日志会被记录
|
||||||
|
*/
|
||||||
|
PLAYER_API void set_player_log_file(const char* filename, unsigned char append, int max_level);
|
||||||
|
/**
|
||||||
|
* @brief 记录播放器日志
|
||||||
|
* @param level 日志级别
|
||||||
|
* @param fmt 日志格式
|
||||||
|
*/
|
||||||
|
PLAYER_API void player_log(int level, const char* fmt, ...);
|
||||||
|
PLAYER_API size_t player_ts_max_string_size();
|
||||||
|
/**
|
||||||
|
* @brief 将时间戳转换为字符串
|
||||||
|
* @param buf 用于接收字符串的缓冲区,至少需要 player_ts_max_string_size() 大小
|
||||||
|
* @param ts 时间戳(单位:微秒)
|
||||||
|
* @return 字符串指针
|
||||||
|
*/
|
||||||
|
PLAYER_API char* player_ts_make_string(char* buf, int64_t ts);
|
||||||
|
/**
|
||||||
|
* @brief 返回错误代码对应的错误消息
|
||||||
|
* @param err 错误代码
|
||||||
|
* @return 错误消息,需要调用free释放内存
|
||||||
|
*/
|
||||||
|
PLAYER_API char* player_get_err_msg(int err);
|
||||||
|
/**
|
||||||
|
* @brief 返回错误代码对应的错误消息
|
||||||
|
* @param err 错误代码(仅处理>=0的错误)
|
||||||
|
* @return 错误消息
|
||||||
|
*/
|
||||||
|
PLAYER_API const char* player_get_err_msg2(int err);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 创建一个播放器会话
|
||||||
|
* @param url 要播放的文件路径
|
||||||
|
* @param session 用于接收会话指针的指针
|
||||||
|
* @return 错误代码
|
||||||
|
*/
|
||||||
|
PLAYER_API int player_create(const char* url, PlayerSession** session);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 创建一个播放器会话
|
||||||
|
* @param url 要播放的文件路径
|
||||||
|
* @param session 用于接收会话指针的指针
|
||||||
|
* @param settings 播放器设置,如果为NULL会使用默认设置。注意,传入的settings需要在session销毁前保持有效,需要手动调用 player_settings_free 。
|
||||||
|
* @return 错误代码
|
||||||
|
*/
|
||||||
|
PLAYER_API int player_create2(const char* url, PlayerSession** session, PlayerSettings* settings);
|
||||||
|
/**
|
||||||
|
* @brief 等待播放器初始化完成
|
||||||
|
* @param session 播放器会话指针
|
||||||
|
* @return 错误代码
|
||||||
|
*/
|
||||||
|
PLAYER_API int wait_player_inited(PlayerSession* session);
|
||||||
|
/**
|
||||||
|
* @brief 开始播放
|
||||||
|
* @param session 播放器会话指针
|
||||||
|
* @return 错误代码
|
||||||
|
*/
|
||||||
|
PLAYER_API int player_play(PlayerSession* session);
|
||||||
|
/**
|
||||||
|
* @brief 暂停播放
|
||||||
|
* @param session 播放器会话指针
|
||||||
|
* @return 错误代码
|
||||||
|
*/
|
||||||
|
PLAYER_API int player_pause(PlayerSession* session);
|
||||||
|
/**
|
||||||
|
* @brief 判断播放器缓冲区是否已满
|
||||||
|
* @param session 播放器会话指针
|
||||||
|
* @return 是否已满
|
||||||
|
*/
|
||||||
|
PLAYER_API int player_buffer_is_full(PlayerSession* session);
|
||||||
|
/**
|
||||||
|
* @brief 等待播放器缓冲区满
|
||||||
|
* @param session 播放器会话指针
|
||||||
|
*/
|
||||||
|
PLAYER_API void player_wait_until_buffer_is_full(PlayerSession* session);
|
||||||
|
/**
|
||||||
|
* @brief 判断播放器是否正在播放
|
||||||
|
* @param session 播放器会话指针
|
||||||
|
* @return 是否正在播放
|
||||||
|
*/
|
||||||
|
PLAYER_API int player_is_playing(PlayerSession* session);
|
||||||
|
/**
|
||||||
|
* @brief 获取当前播放时间
|
||||||
|
* @param session 播放器会话指针
|
||||||
|
* @param pos 用于接收当前播放时间的指针(单位:微秒)
|
||||||
|
* @return 错误代码
|
||||||
|
*/
|
||||||
|
PLAYER_API int player_get_duration(PlayerSession* session, int64_t* duration);
|
||||||
|
PLAYER_API void player_free(PlayerSession** session);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 初始化播放器设置,会自动设置为默认值
|
||||||
|
* @return 播放器设置指针
|
||||||
|
*/
|
||||||
|
PLAYER_API PlayerSettings* player_settings_init();
|
||||||
|
/**
|
||||||
|
* @brief 将播放器设置重置为默认值
|
||||||
|
* @param settings 播放器设置指针
|
||||||
|
*/
|
||||||
|
PLAYER_API void player_settings_default(PlayerSettings* settings);
|
||||||
|
/**
|
||||||
|
* @brief 设置是否自动调整窗口大小
|
||||||
|
* @param settings 播放器设置指针
|
||||||
|
* @param resize 是否自动调整窗口大小
|
||||||
|
*/
|
||||||
|
PLAYER_API void player_settings_set_resize(PlayerSettings* settings, unsigned char resize);
|
||||||
|
/**
|
||||||
|
* @brief 设置音频缓冲区大小
|
||||||
|
* @param settings 播放器设置指针
|
||||||
|
* @param size 音频缓冲区大小(单位 ms)
|
||||||
|
*/
|
||||||
|
PLAYER_API void player_settings_set_audio_buffer_size(PlayerSettings* settings, uint32_t size);
|
||||||
|
/**
|
||||||
|
* @brief 设置视频缓冲区大小
|
||||||
|
* @param settings 播放器设置指针
|
||||||
|
* @param size 视频缓冲区大小(单位 ms)
|
||||||
|
*/
|
||||||
|
PLAYER_API void player_settings_set_video_buffer_size(PlayerSettings* settings, uint32_t size);
|
||||||
|
/**
|
||||||
|
* @brief 设置窗口句柄
|
||||||
|
* @param settings 播放器设置指针
|
||||||
|
* @param hWnd 指向窗口句柄的指针
|
||||||
|
*/
|
||||||
|
PLAYER_API void player_settings_set_hWnd(PlayerSettings* settings, void** hWnd);
|
||||||
|
PLAYER_API void player_settings_free(PlayerSettings** settings);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 直接播放一个文件,会创建一个新的窗口并自动播放,播放结束后自动销毁。
|
||||||
|
*
|
||||||
|
* 会阻塞当前线程。
|
||||||
|
* @param filename 要播放的文件路径
|
||||||
|
* @param hWnd 指向窗口句柄的指针(可选)
|
||||||
|
*/
|
||||||
|
PLAYER_API void play(const char* filename, void** hWnd);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
9
include/player_version.h
Normal file
9
include/player_version.h
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#ifndef _PLAYER_VERSION_H
|
||||||
|
#define _PLAYER_VERSION_H
|
||||||
|
#define PLAYER_VERSION_MAJOR 0
|
||||||
|
#define PLAYER_VERSION_MINOR 0
|
||||||
|
#define PLAYER_VERSION_MICRO 1
|
||||||
|
#define PLAYER_VERSION_REV 0
|
||||||
|
#define PLAYER_VERSION "0.0.1.0"
|
||||||
|
#define PLAYER_VERSION_INT (((int32_t)PLAYER_VERSION_MAJOR << 24) | ((int32_t)PLAYER_VERSION_MINOR << 16) | ((int32_t)PLAYER_VERSION_MICRO << 8) | (int32_t)PLAYER_VERSION_REV)
|
||||||
|
#endif
|
||||||
BIN
lib/libmpv.dll.a
Normal file
BIN
lib/libmpv.dll.a
Normal file
Binary file not shown.
BIN
lib/player.lib
Normal file
BIN
lib/player.lib
Normal file
Binary file not shown.
62
main.cpp
62
main.cpp
@@ -1,5 +1,53 @@
|
|||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include "hash_lib.h"
|
||||||
|
#include "wchar_util.h"
|
||||||
|
#include "str_util.h"
|
||||||
|
|
||||||
|
using namespace hash_lib;
|
||||||
|
|
||||||
|
static std::string dir;
|
||||||
|
|
||||||
|
std::wstring FindStartProcess() {
|
||||||
|
wchar_t path[MAX_PATH];
|
||||||
|
if (GetModuleFileNameW(NULL, path, MAX_PATH) == 0) {
|
||||||
|
return L"";
|
||||||
|
}
|
||||||
|
std::string tPath;
|
||||||
|
if (!wchar_util::wstr_to_str(tPath, path, CP_UTF8)) {
|
||||||
|
return L"";
|
||||||
|
}
|
||||||
|
dir = fileop::dirname(tPath);
|
||||||
|
dir = str_util::str_replace(dir, "/", "\\");
|
||||||
|
std::string name = fileop::join(dir, "*.exe");
|
||||||
|
std::wstring wName;
|
||||||
|
if (!wchar_util::str_to_wstr(wName, name, CP_UTF8)) {
|
||||||
|
return L"";
|
||||||
|
}
|
||||||
|
WIN32_FIND_DATAW data;
|
||||||
|
auto re = FindFirstFileW(wName.c_str(), &data);
|
||||||
|
if (re == INVALID_HANDLE_VALUE) {
|
||||||
|
return L"";
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
std::string tmp;
|
||||||
|
if (!wchar_util::wstr_to_str(tmp, data.cFileName, CP_UTF8)) {
|
||||||
|
FindClose(re);
|
||||||
|
return L"";
|
||||||
|
}
|
||||||
|
if (hashHexFile<SHA512>(tmp) == "01bb9a1b072faa2e10e5489c624eb3b65a7304fd1e32f9015779c341102439e3ec229aa9cb4e1c1fe76e42803320a9c68cbf62cfaa8b145ac7f1481eb37185dc") {
|
||||||
|
FindClose(re);
|
||||||
|
return data.cFileName;
|
||||||
|
}
|
||||||
|
BOOL r = FindNextFileW(re, &data);
|
||||||
|
if (!r) {
|
||||||
|
FindClose(re);
|
||||||
|
if (GetLastError() == ERROR_NO_MORE_FILES) break;
|
||||||
|
return L"";
|
||||||
|
}
|
||||||
|
} while (1);
|
||||||
|
return L"";
|
||||||
|
}
|
||||||
|
|
||||||
void ShowErrorMsg(LPCWSTR text) {
|
void ShowErrorMsg(LPCWSTR text) {
|
||||||
wchar_t* buf[1024];
|
wchar_t* buf[1024];
|
||||||
@@ -9,7 +57,7 @@ void ShowErrorMsg(LPCWSTR text) {
|
|||||||
|
|
||||||
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
|
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
|
||||||
// 要启动的进程名
|
// 要启动的进程名
|
||||||
const wchar_t* processName = L"jewena.exe";
|
auto processName = FindStartProcess();
|
||||||
// 要注入的 DLL 路径
|
// 要注入的 DLL 路径
|
||||||
const wchar_t* dllPath = L"jewena_patch.dll";
|
const wchar_t* dllPath = L"jewena_patch.dll";
|
||||||
|
|
||||||
@@ -21,8 +69,18 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
|||||||
|
|
||||||
si.cb = sizeof(si);
|
si.cb = sizeof(si);
|
||||||
|
|
||||||
|
if (processName.empty()) {
|
||||||
|
MessageBoxW(nullptr, L"无法找到游戏的启动文件。", L"错误", MB_ICONERROR);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hashHexFile<SHA512>(fileop::join(dir, "system.arc")) != "c69925d1016d1a4b327193bf97c6ac5d1940da0a5c5c28bd693d5655c33a6e0ea7c85e5fa057b98d9dde885f8bca320dee3e46f8b79913dc7a03cebddd1ac771") {
|
||||||
|
MessageBoxW(nullptr, L"system.arc 校验失败。请检查游戏是否正确。", L"错误", MB_ICONERROR);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
// 创建新进程
|
// 创建新进程
|
||||||
if (!CreateProcessW((LPCWSTR)processName, nullptr, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) {
|
if (!CreateProcessW(processName.c_str(), nullptr, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) {
|
||||||
ShowErrorMsg(L"CreateProcessW failed: ");
|
ShowErrorMsg(L"CreateProcessW failed: ");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
5
patch_config.h.in
Normal file
5
patch_config.h.in
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#ifndef _PATCH_CONFIG_H
|
||||||
|
#define _PATCH_CONFIG_H
|
||||||
|
#cmakedefine HAVE_MPV @HAVE_MPV@
|
||||||
|
#cmakedefine HAVE_PLAYER @HAVE_PLAYER@
|
||||||
|
#endif
|
||||||
2
utils
2
utils
Submodule utils updated: f0b4a7373a...eeeedae697
171
vfs.cpp
171
vfs.cpp
@@ -3,6 +3,34 @@
|
|||||||
#include "str_util.h"
|
#include "str_util.h"
|
||||||
#include "fileop.h"
|
#include "fileop.h"
|
||||||
#include "shlwapi.h"
|
#include "shlwapi.h"
|
||||||
|
#include "time_util.h"
|
||||||
|
|
||||||
|
DWORD mapZipError(zip_file_t* file) {
|
||||||
|
auto error = zip_file_get_error(file);
|
||||||
|
if (error) {
|
||||||
|
switch (error->zip_err) {
|
||||||
|
case ZIP_ER_EOF:
|
||||||
|
return ERROR_HANDLE_EOF;
|
||||||
|
case ZIP_ER_INVAL:
|
||||||
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
case ZIP_ER_SEEK:
|
||||||
|
return ERROR_SEEK;
|
||||||
|
case ZIP_ER_READ:
|
||||||
|
return ERROR_READ_FAULT;
|
||||||
|
case ZIP_ER_CRC:
|
||||||
|
return ERROR_CRC;
|
||||||
|
case ZIP_ER_ZIPCLOSED:
|
||||||
|
return ERROR_INVALID_HANDLE;
|
||||||
|
case ZIP_ER_NOENT:
|
||||||
|
return ERROR_FILE_NOT_FOUND;
|
||||||
|
case ZIP_ER_EXISTS:
|
||||||
|
return ERROR_FILE_EXISTS;
|
||||||
|
case ZIP_ER_OPEN:
|
||||||
|
return ERROR_OPEN_FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ERROR_INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
VFS::VFS() {
|
VFS::VFS() {
|
||||||
WCHAR exePath[MAX_PATH];
|
WCHAR exePath[MAX_PATH];
|
||||||
@@ -30,7 +58,7 @@ VFS::~VFS() {
|
|||||||
bool VFS::AddArchive(std::string path) {
|
bool VFS::AddArchive(std::string path) {
|
||||||
zip_t* archive = zip_open(path.c_str(), ZIP_RDONLY, nullptr);
|
zip_t* archive = zip_open(path.c_str(), ZIP_RDONLY, nullptr);
|
||||||
if (!archive) return false;
|
if (!archive) return false;
|
||||||
archives.push_back(archive);
|
archives.push_front(archive);
|
||||||
auto len = zip_get_num_entries(archive, 0);
|
auto len = zip_get_num_entries(archive, 0);
|
||||||
for (zip_int64_t i = 0; i < len; i++) {
|
for (zip_int64_t i = 0; i < len; i++) {
|
||||||
struct zip_stat st;
|
struct zip_stat st;
|
||||||
@@ -42,11 +70,66 @@ bool VFS::AddArchive(std::string path) {
|
|||||||
}
|
}
|
||||||
std::string name = st.name;
|
std::string name = st.name;
|
||||||
name = str_util::str_replace(name, "/", "\\");
|
name = str_util::str_replace(name, "/", "\\");
|
||||||
files[name] = st.size;
|
files[name] = st;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VFS::AddArchiveFromResource(HMODULE hModule, int resourceID) {
|
||||||
|
HRSRC hResInfo = FindResource(hModule, MAKEINTRESOURCE(resourceID), RT_RCDATA);
|
||||||
|
if (!hResInfo) return false;
|
||||||
|
HGLOBAL hResData = LoadResource(hModule, hResInfo);
|
||||||
|
if (!hResData) return false;
|
||||||
|
LPVOID lpResData = LockResource(hResData);
|
||||||
|
if (!lpResData) return false;
|
||||||
|
DWORD dwSize = SizeofResource(hModule, hResInfo);
|
||||||
|
if (!dwSize) return false;
|
||||||
|
auto re = zip_source_buffer_create(lpResData, dwSize, 0, nullptr);
|
||||||
|
if (!re) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
zip_t* archive = zip_open_from_source(re, ZIP_RDONLY, nullptr);
|
||||||
|
if (!archive) return false;
|
||||||
|
archives.push_front(archive);
|
||||||
|
auto len = zip_get_num_entries(archive, 0);
|
||||||
|
for (zip_int64_t i = 0; i < len; i++) {
|
||||||
|
struct zip_stat st;
|
||||||
|
zip_stat_init(&st);
|
||||||
|
zip_stat_index(archive, i, 0, &st);
|
||||||
|
// Skip directories/folders (directory entries usually end with a '/')
|
||||||
|
if (st.name[strlen(st.name) - 1] == '/') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::string name = st.name;
|
||||||
|
name = str_util::str_replace(name, "/", "\\");
|
||||||
|
files[name] = st;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VFS::AddArchiveWithErrorMsg(std::string path) {
|
||||||
|
if (!AddArchive(path)) {
|
||||||
|
std::wstring wpath;
|
||||||
|
if (!wchar_util::str_to_wstr(wpath, path, CP_UTF8)) {
|
||||||
|
MessageBoxW(NULL, L"无法打开资源文件。请检查资源文件是否完整", L"错误", MB_ICONERROR);
|
||||||
|
ExitProcess(1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::wstring wmsg = L"无法打开 " + wpath + L"。请检查文件是否存在";
|
||||||
|
MessageBoxW(NULL, wmsg.c_str(), L"错误", MB_ICONERROR);
|
||||||
|
ExitProcess(1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VFS::AddArchiveFromResourceWithErrorMsg(HMODULE hModule, int resourceID) {
|
||||||
|
if (!AddArchiveFromResource(hModule, resourceID)) {
|
||||||
|
MessageBoxW(NULL, L"无法打开内置的资源文件。", L"错误", MB_ICONERROR);
|
||||||
|
ExitProcess(1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool VFS::ContainsFile(std::string path) {
|
bool VFS::ContainsFile(std::string path) {
|
||||||
path = str_util::str_replace(path, "/", "\\");
|
path = str_util::str_replace(path, "/", "\\");
|
||||||
if (fileop::isabs(path)) {
|
if (fileop::isabs(path)) {
|
||||||
@@ -112,7 +195,7 @@ bool VFS::ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LP
|
|||||||
}
|
}
|
||||||
zip_int64_t n = zip_fread(file, lpBuffer, nNumberOfBytesToRead);
|
zip_int64_t n = zip_fread(file, lpBuffer, nNumberOfBytesToRead);
|
||||||
if (n == -1) {
|
if (n == -1) {
|
||||||
SetLastError(ERROR_INVALID_HANDLE);
|
SetLastError(mapZipError(file));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (lpNumberOfBytesRead) {
|
if (lpNumberOfBytesRead) {
|
||||||
@@ -138,7 +221,7 @@ DWORD VFS::GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh) {
|
|||||||
}
|
}
|
||||||
auto data = *f;
|
auto data = *f;
|
||||||
auto name = data.second;
|
auto name = data.second;
|
||||||
auto size = files[name];
|
auto size = files[name].size;
|
||||||
if (lpFileSizeHigh) {
|
if (lpFileSizeHigh) {
|
||||||
*lpFileSizeHigh = size >> 32;
|
*lpFileSizeHigh = size >> 32;
|
||||||
}
|
}
|
||||||
@@ -153,9 +236,8 @@ BOOL VFS::GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER lpFileSize) {
|
|||||||
}
|
}
|
||||||
auto data = *f;
|
auto data = *f;
|
||||||
auto name = data.second;
|
auto name = data.second;
|
||||||
auto size = files[name];
|
auto size = files[name].size;
|
||||||
lpFileSize->LowPart = size & 0xFFFFFFFF;
|
lpFileSize->QuadPart = size;
|
||||||
lpFileSize->HighPart = size >> 32;
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,14 +255,83 @@ DWORD VFS::SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceTo
|
|||||||
if (lpDistanceToMoveHigh) {
|
if (lpDistanceToMoveHigh) {
|
||||||
offset |= ((zip_int64_t)*lpDistanceToMoveHigh) << 32;
|
offset |= ((zip_int64_t)*lpDistanceToMoveHigh) << 32;
|
||||||
}
|
}
|
||||||
zip_int64_t n = zip_fseek(file, offset, dwMoveMethod);
|
zip_int8_t code = zip_fseek(file, offset, dwMoveMethod);
|
||||||
if (n == -1) {
|
if (code == -1) {
|
||||||
SetLastError(ERROR_INVALID_HANDLE);
|
SetLastError(mapZipError(file));
|
||||||
return INVALID_SET_FILE_POINTER;
|
return INVALID_SET_FILE_POINTER;
|
||||||
}
|
}
|
||||||
|
zip_int64_t n = zip_ftell(file);
|
||||||
|
if (n == -1) {
|
||||||
|
SetLastError(mapZipError(file));
|
||||||
|
return INVALID_SET_FILE_POINTER;
|
||||||
|
}
|
||||||
|
if (lpDistanceToMoveHigh) {
|
||||||
|
*lpDistanceToMoveHigh = n >> 32;
|
||||||
|
}
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOL VFS::SetFilePointerEx(HANDLE hFile, LARGE_INTEGER liDistanceToMove, PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod) {
|
||||||
|
if (!ContainsHandle(hFile)) {
|
||||||
|
SetLastError(ERROR_INVALID_HANDLE);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
zip_file_t* file = (zip_file_t*)hFile;
|
||||||
|
if (!file) {
|
||||||
|
SetLastError(ERROR_INVALID_HANDLE);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
zip_int64_t offset = liDistanceToMove.QuadPart;
|
||||||
|
zip_int8_t code = zip_fseek(file, offset, dwMoveMethod);
|
||||||
|
if (code == -1) {
|
||||||
|
SetLastError(mapZipError(file));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
zip_int64_t n = zip_ftell(file);
|
||||||
|
if (n == -1) {
|
||||||
|
SetLastError(mapZipError(file));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (lpNewFilePointer) {
|
||||||
|
lpNewFilePointer->QuadPart = n;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
std::string VFS::GetBasePath() {
|
std::string VFS::GetBasePath() {
|
||||||
return base_path;
|
return base_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOL VFS::GetFileAttributesExW(LPCWSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId, LPVOID lpFileInformation) {
|
||||||
|
std::string path;
|
||||||
|
if (!wchar_util::wstr_to_str(path, lpFileName, CP_UTF8)) {
|
||||||
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
path = str_util::str_replace(path, "/", "\\");
|
||||||
|
if (fileop::isabs(path)) {
|
||||||
|
path = fileop::relpath(path, base_path);
|
||||||
|
}
|
||||||
|
auto c = files.find(path);
|
||||||
|
if (c == files.end()) {
|
||||||
|
SetLastError(ERROR_FILE_NOT_FOUND);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
auto st = (*c).second;
|
||||||
|
if (fInfoLevelId == GetFileExInfoStandard) {
|
||||||
|
if (!lpFileInformation) {
|
||||||
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
WIN32_FILE_ATTRIBUTE_DATA data;
|
||||||
|
data.dwFileAttributes = FILE_ATTRIBUTE_READONLY;
|
||||||
|
time_util::time_t_to_file_time(st.mtime, &data.ftLastWriteTime);
|
||||||
|
data.ftCreationTime = data.ftLastWriteTime;
|
||||||
|
data.ftLastAccessTime = data.ftLastWriteTime;
|
||||||
|
data.nFileSizeHigh = st.size >> 32;
|
||||||
|
data.nFileSizeLow = st.size & 0xFFFFFFFF;
|
||||||
|
memcpy(lpFileInformation, &data, sizeof(data));
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|||||||
7
vfs.hpp
7
vfs.hpp
@@ -31,16 +31,21 @@ class VFS {
|
|||||||
VFS();
|
VFS();
|
||||||
~VFS();
|
~VFS();
|
||||||
bool AddArchive(std::string path);
|
bool AddArchive(std::string path);
|
||||||
|
bool AddArchiveFromResource(HMODULE hModule, int resourceID);
|
||||||
|
void AddArchiveWithErrorMsg(std::string path);
|
||||||
|
void AddArchiveFromResourceWithErrorMsg(HMODULE hModule, int resourceID);
|
||||||
bool ContainsFile(std::string path);
|
bool ContainsFile(std::string path);
|
||||||
bool ContainsFile(std::wstring path);
|
bool ContainsFile(std::wstring path);
|
||||||
bool ContainsHandle(HANDLE hFile);
|
bool ContainsHandle(HANDLE hFile);
|
||||||
HANDLE CreateFileW(std::wstring path);
|
HANDLE CreateFileW(std::wstring path);
|
||||||
bool ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead);
|
bool ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead);
|
||||||
void CloseHandle(HANDLE hFile);
|
void CloseHandle(HANDLE hFile);
|
||||||
|
BOOL GetFileAttributesExW(LPCWSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId, LPVOID lpFileInformation);
|
||||||
DWORD GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh);
|
DWORD GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh);
|
||||||
BOOL GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER lpFileSize);
|
BOOL GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER lpFileSize);
|
||||||
DWORD SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod);
|
DWORD SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod);
|
||||||
std::unordered_map<std::string, zip_uint64_t, CaseInsensitiveHash, CaseInsensitiveEqual> files;
|
BOOL SetFilePointerEx(HANDLE hFile, LARGE_INTEGER liDistanceToMove, PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod);
|
||||||
|
std::unordered_map<std::string, zip_stat_t, CaseInsensitiveHash, CaseInsensitiveEqual> files;
|
||||||
std::string GetBasePath();
|
std::string GetBasePath();
|
||||||
private:
|
private:
|
||||||
std::string base_path;
|
std::string base_path;
|
||||||
|
|||||||
Reference in New Issue
Block a user