Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ endif()
add_subdirectory (extern)
add_subdirectory (components)

# This is currently only for the MSVC libmpv, might be a better place to put library or to link/find it
link_directories(${CMAKE_CURRENT_BINARY_DIR}/components)

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})

add_subdirectory(apps/freeablo)
Expand Down
2 changes: 1 addition & 1 deletion apps/freeablo/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ add_library(freeablo_lib # split into a library so I can link to it from tests
fasavegame/gameloader.cpp
)

target_link_libraries(freeablo_lib PUBLIC NuklearMisc Render Audio Serial Input Random enet::enet)
target_link_libraries(freeablo_lib PUBLIC NuklearMisc Render Audio Video Serial Input Random enet::enet)
target_include_directories(freeablo_lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

add_executable(freeablo main.cpp)
Expand Down
21 changes: 21 additions & 0 deletions apps/freeablo/engine/enginemain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@ namespace Engine
mainThread.join();
}

void EngineMain::playVideo(const std::string& path)
{
Engine::ThreadManager::get()->playVideo(std::string(path));
while (!Engine::ThreadManager::get()->waitForVideoComplete(100))
{
mInputManager->update(false);
}
}

void EngineMain::runGameLoop(const bpo::variables_map& variables, const std::string& pathEXE)
{
FARender::Renderer& renderer = *FARender::Renderer::get();
Expand All @@ -81,6 +90,10 @@ namespace Engine
return;
}

// Play the intro cinematics.
playVideo("gendata/logo.smk");
playVideo("gendata/diablo1.smk");

FAWorld::ItemFactory itemFactory(*mExe, Random::DummyRng::instance);
mPlayerFactory = boost::make_unique<FAWorld::PlayerFactory>(*mExe, itemFactory);
renderer.loadFonts(*mExe);
Expand Down Expand Up @@ -243,6 +256,14 @@ namespace Engine

void EngineMain::notify(KeyboardInputAction action)
{
// Only handle the esc key when playing video.
if (Engine::ThreadManager::get()->videoInProgress())
{
if (action == KeyboardInputAction::pause)
Engine::ThreadManager::get()->stopVideo();
return;
}

if (mGuiManager->isPauseBlocked())
return;

Expand Down
1 change: 1 addition & 0 deletions apps/freeablo/engine/enginemain.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ namespace Engine

private:
void runGameLoop(const boost::program_options::variables_map& variables, const std::string& pathEXE);
void playVideo(const std::string& path);

private:
static EngineMain* singletonInstance;
Expand Down
65 changes: 65 additions & 0 deletions apps/freeablo/engine/threadmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <iostream>

#include <input/inputmanager.h>
#include <video/video.h>

#include "../farender/renderer.h"

Expand All @@ -19,6 +20,7 @@ namespace Engine
const int MAXIMUM_DURATION_IN_MS = 1000;
Input::InputManager* inputManager = Input::InputManager::get();
FARender::Renderer* renderer = FARender::Renderer::get();
Video::init();

Message message;

Expand All @@ -27,6 +29,19 @@ namespace Engine

while (true)
{
if (mvideoQueue.pop(message))
handleMessage(message);

while (videoInProgress())
{
// Don't render frames, or even pop normal queue messages while video in progress.
// However still need to listen for input and messages to start/stop video.
Video::update();
inputManager->poll();
if (mvideoQueue.pop(message))
handleMessage(message);
}

mSpritesToPreload.clear();

while (mQueue.pop(message))
Expand All @@ -52,6 +67,7 @@ namespace Engine
}

renderer->cleanup();
Video::quit();
}

void ThreadManager::playMusic(const std::string& path)
Expand Down Expand Up @@ -85,6 +101,36 @@ namespace Engine
mQueue.push(message);
}

void ThreadManager::playVideo(const std::string& path)
{
Message message;
message.type = ThreadState::PLAY_VIDEO;
message.data.videoPath = new std::string(path);
mVideoPending = true;

mvideoQueue.push(message);
}

void ThreadManager::stopVideo()
{
Message message;
message.type = ThreadState::STOP_VIDEO;
mvideoQueue.push(message);
}

bool ThreadManager::waitForVideoComplete(int ms)
{
auto tWait = std::chrono::milliseconds(ms);
std::unique_lock<std::mutex> lk(mVideoPendingMutex);
if (mVideoStartedCV.wait_for(lk, tWait, [this] { return !mVideoPending; }))
{
lk.unlock();
if (Video::waitForVideoComplete(ms))
return true;
}
return false;
}

void ThreadManager::sendRenderState(FARender::RenderState* state)
{
Message message;
Expand Down Expand Up @@ -127,6 +173,25 @@ namespace Engine
break;
}

case ThreadState::PLAY_VIDEO:
{
mAudioManager.stopSound();

Video::playVideo(*message.data.videoPath);
delete message.data.videoPath;

std::unique_lock<std::mutex> lk(mVideoPendingMutex);
mVideoPending = false;
mVideoStartedCV.notify_all();
break;
}

case ThreadState::STOP_VIDEO:
{
Video::stopVideo();
break;
}

case ThreadState::RENDER_STATE:
{
if (mRenderState && mRenderState != message.data.renderState)
Expand Down
14 changes: 14 additions & 0 deletions apps/freeablo/engine/threadmanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#include <boost/next_prior.hpp>

#include <boost/lockfree/spsc_queue.hpp>
#include <condition_variable>
#include <mutex>
#include <string>

#include "../faaudio/audiomanager.h"
Expand All @@ -22,6 +24,8 @@ namespace Engine
PLAY_MUSIC,
PLAY_SOUND,
STOP_SOUND,
PLAY_VIDEO,
STOP_VIDEO,
RENDER_STATE,
PRELOAD_SPRITES
};
Expand All @@ -34,6 +38,7 @@ namespace Engine
{
std::string* musicPath;
std::string* soundPath;
std::string* videoPath;
FARender::RenderState* renderState;
std::vector<uint32_t>* preloadSpriteIds;
} data;
Expand All @@ -48,17 +53,26 @@ namespace Engine
void playMusic(const std::string& path);
void playSound(const std::string& path);
void stopSound();
void playVideo(const std::string& path);
void stopVideo();
void sendRenderState(FARender::RenderState* state);
void sendSpritesForPreload(std::vector<uint32_t> sprites);
bool waitForVideoComplete(int ms);
bool videoInProgress() { return !waitForVideoComplete(0); }

private:
void handleMessage(const Message& message);

static ThreadManager* mThreadManager; ///< Singleton instance
boost::lockfree::spsc_queue<Message, boost::lockfree::capacity<100>> mQueue;
boost::lockfree::spsc_queue<Message, boost::lockfree::capacity<10>> mvideoQueue;
FARender::RenderState* mRenderState;
FAAudio::AudioManager mAudioManager;

std::vector<uint32_t> mSpritesToPreload;

std::mutex mVideoPendingMutex;
bool mVideoPending = false;
std::condition_variable mVideoStartedCV;
};
}
25 changes: 25 additions & 0 deletions cmake/FindMPV.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# - Try to find libmpv
# Once done this will define
# MPV_FOUND - System has LibMPV
# MPV_VERSION - LibMPV version
# MPV_INCLUDE_DIRS - The LibMPV include directories
# MPV_LIBRARIES - The libraries needed to use LibMPV
# MPV_DEFINITIONS - Compiler switches required for using LibMPV

find_package(PkgConfig)
pkg_check_modules(PC_MPV QUIET mpv)
set(MPV_DEFINITIONS ${PC_MPV_CFLAGS_OTHER})
set(MPV_VERSION ${PC_MPV_VERSION})

find_path(MPV_INCLUDE_DIR mpv/client.h HINTS ${PC_MPV_INCLUDEDIR} ${PC_MPV_INCLUDE_DIRS} PATH_SUFFIXES mpv)

find_library(MPV_LIBRARY NAMES mpv libmpv HINTS ${PC_MPV_LIBDIR} ${PC_MPV_LIBRARY_DIRS})

set(MPV_LIBRARIES ${MPV_LIBRARY})
set(MPV_INCLUDE_DIRS ${MPV_INCLUDE_DIR})

include(FindPackageHandleStandardArgs)
# handle the QUIETLY and REQUIRED arguments and set MPV_FOUND to TRUE if all listed variables are TRUE
find_package_handle_standard_args(MPV DEFAULT_MSG MPV_LIBRARY MPV_INCLUDE_DIR)

mark_as_advanced(MPV_INCLUDE_DIR MPV_LIBRARY)
78 changes: 78 additions & 0 deletions components/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,84 @@ add_library(Audio
target_link_libraries(Audio FAIO SDL_mixer::SDL_mixer)
set_target_properties(Audio PROPERTIES COMPILE_FLAGS "${FA_COMPILER_FLAGS}")

if (MSVC)
# Download and prepare MSYS2 libmpv for MSVC conversion.
# https://github.com/mpv-player/mpv/blob/master/DOCS/compile-windows.md#linking-libmpv-with-msvc-programs
set(MPV_MSYS2_VERSION "mpv-dev-20181002")
set(MPV_MSYS2_URL "https://mpv.srsfckn.biz/${MPV_MSYS2_VERSION}.7z")
set(MPV_MSYS2_BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}/${MPV_MSYS2_VERSION}")
set(MPV_MSYS2_7Z_FILE "${MPV_MSYS2_BASE_DIR}.7z")

if (CMAKE_SIZEOF_VOID_P EQUAL 8)
# 64 bit
set(MPV_MSYS2_LIB_DIR "${MPV_MSYS2_BASE_DIR}/x86_64")
set(MSVC_LIB_MACHINE x64)
elseif (CMAKE_SIZEOF_VOID_P EQUAL 4)
# 32 bit
set(MPV_MSYS2_LIB_DIR "${MPV_MSYS2_BASE_DIR}/i686")
set(MSVC_LIB_MACHINE x86)
endif()

if (NOT EXISTS ${MPV_MSYS2_BASE_DIR})
message("Downloading MSYS2 mpv libraries ${MPV_MSYS2_URL}")
file(DOWNLOAD ${MPV_MSYS2_URL} ${MPV_MSYS2_7Z_FILE} SHOW_PROGRESS)
message("Extracting MSYS2 mpv libraries...")
file(MAKE_DIRECTORY ${MPV_MSYS2_BASE_DIR})
execute_process(
COMMAND ${CMAKE_COMMAND} -E tar xf ${MPV_MSYS2_7Z_FILE} --format=7zip
WORKING_DIRECTORY ${MPV_MSYS2_BASE_DIR}
RESULT_VARIABLE res)
if (res)
message(FATAL_ERROR "libmpv extraction failed: ${res}")
endif()

# Need to add "LIBRARY MPV-1\nEXPORTS\n" to the start of the def file
# https://github.com/mpv-player/mpv/issues/5007#issuecomment-337431977
file(READ ${MPV_MSYS2_LIB_DIR}/libmpv.def inital_libmpv_def_file)
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/libmpv.def "LIBRARY MPV-1\nEXPORTS\n${inital_libmpv_def_file}" )

# Copy mpv-1.dll to build dir, this means exe's must be run from this location (otherwise could copy next to exe's or add to path).
file(COPY ${MPV_MSYS2_LIB_DIR}/mpv-1.dll DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/..)
endif()

set(MPV_FOUND TRUE)
set(MPV_LIBRARIES libmpv)
set(MPV_INCLUDE_DIRS "${MPV_MSYS2_BASE_DIR}/include")
set(MPV_DEFINITIONS "")
else (MSVC)
include(FindMPV)
endif(MSVC)

IF (MPV_FOUND)
add_library(Video
video/video.h
video/mpv_sdl_backend.cpp
)
if (MSVC)
# Convert MSYS2 lib to MSVC format, possibly could use built in cmake module GNUtoMS_lib.cmake instead.
add_custom_command(
TARGET Video
PRE_BUILD
COMMAND lib ARGS /def:${CMAKE_CURRENT_BINARY_DIR}/libmpv.def /name:mpv-1.dll /out:${CMAKE_CURRENT_BINARY_DIR}/libmpv.lib /machine:${MSVC_LIB_MACHINE})
endif(MSVC)
target_link_libraries(Video FAIO "${MPV_LIBRARIES}" SDL2::SDL2)
target_include_directories(Video PRIVATE "${MPV_INCLUDE_DIRS}")
set_target_properties(Video PROPERTIES COMPILE_FLAGS "${FA_COMPILER_FLAGS} ${MPV_DEFINITIONS}")
ELSE (MPV_FOUND)
message("MPV not found, using stub implemenation, video disabled")
add_library(Video
video/video.h
video/stubbackend.cpp
)
set_target_properties(Video PROPERTIES COMPILE_FLAGS "${FA_COMPILER_FLAGS}")
ENDIF (MPV_FOUND)

add_executable(VideoTest
video/videotest.cpp
)
target_link_libraries(VideoTest Video SDL2::SDL2)
set_target_properties(VideoTest PROPERTIES COMPILE_FLAGS "${FA_COMPILER_FLAGS}")

add_library(Settings
settings/settings.h
settings/settings.cpp
Expand Down
Loading