Browse Source

🎉 Stream videos on Linux and FreeBSD

Previously, we always loaded entire videos in memory before playing them.

`libsmacker` does not provide a streaming API but it does provide a
`FILE *` file pointer API.

We now take advantage of the `FILE *` API by streaming the videos on
platforms that support `fopencookie`.

This means faster startup and less memory usage on these platforms.
pull/1786/head
Gleb Mazovetskiy 5 years ago committed by Anders Jenbo
parent
commit
c770ae676f
  1. 1
      CMakeLists.txt
  2. 38
      Source/movie.cpp
  3. 74
      Source/storm/storm_file_wrapper.cpp
  4. 17
      Source/storm/storm_file_wrapper.h
  5. 27
      Source/storm/storm_svid.cpp
  6. 2
      Source/storm/storm_svid.h

1
CMakeLists.txt

@ -388,6 +388,7 @@ set(devilutionx_SRCS
Source/dvlnet/loopback.cpp Source/dvlnet/loopback.cpp
Source/dvlnet/packet.cpp Source/dvlnet/packet.cpp
Source/storm/storm.cpp Source/storm/storm.cpp
Source/storm/storm_file_wrapper.cpp
Source/storm/storm_net.cpp Source/storm/storm_net.cpp
Source/storm/storm_sdl_rw.cpp Source/storm/storm_sdl_rw.cpp
Source/storm/storm_svid.cpp Source/storm/storm_svid.cpp

38
Source/movie.cpp

@ -37,28 +37,28 @@ void play_movie(const char *pszMovie, bool user_can_close)
effects_play_sound("Sfx\\Misc\\blank.wav"); effects_play_sound("Sfx\\Misc\\blank.wav");
#endif #endif
SVidPlayBegin(pszMovie, loop_movie ? 0x100C0808 : 0x10280808, &video_stream); if (SVidPlayBegin(pszMovie, loop_movie ? 0x100C0808 : 0x10280808, &video_stream)) {
tagMSG Msg; tagMSG Msg;
while (video_stream != nullptr && movie_playing) { while (movie_playing) {
while (movie_playing && FetchMessage(&Msg)) { while (movie_playing && FetchMessage(&Msg)) {
switch (Msg.message) { switch (Msg.message) {
case DVL_WM_KEYDOWN: case DVL_WM_KEYDOWN:
case DVL_WM_LBUTTONDOWN: case DVL_WM_LBUTTONDOWN:
case DVL_WM_RBUTTONDOWN: case DVL_WM_RBUTTONDOWN:
if (user_can_close || (Msg.message == DVL_WM_KEYDOWN && Msg.wParam == DVL_VK_ESCAPE)) if (user_can_close || (Msg.message == DVL_WM_KEYDOWN && Msg.wParam == DVL_VK_ESCAPE))
movie_playing = false; movie_playing = false;
break; break;
case DVL_WM_QUIT: case DVL_WM_QUIT:
SVidPlayEnd(video_stream); SVidPlayEnd(video_stream);
diablo_quit(0); diablo_quit(0);
break; break;
}
} }
if (!SVidPlayContinue())
break;
} }
if (!SVidPlayContinue())
break;
}
if (video_stream != nullptr)
SVidPlayEnd(video_stream); SVidPlayEnd(video_stream);
}
#ifndef NOSOUND #ifndef NOSOUND
sound_disable_music(false); sound_disable_music(false);

74
Source/storm/storm_file_wrapper.cpp

@ -0,0 +1,74 @@
#include "storm_file_wrapper.h"
#ifdef DEVILUTIONX_STORM_FILE_WRAPPER_AVAILABLE
#include "storm/storm.h"
#include "utils/log.hpp"
namespace devilution {
extern "C" {
#ifdef DEVILUTIONX_STORM_FILE_WRAPPER_IMPL_FOPENCOOKIE
ssize_t SFileCookieRead(void *cookie, char *buf, size_t nbytes)
{
DWORD numRead = 0;
if (!SFileReadFile(static_cast<HANDLE>(cookie), buf, nbytes, &numRead, nullptr)) {
const DWORD errCode = SErrGetLastError();
if (errCode != STORM_ERROR_HANDLE_EOF) {
Log("SFileRwRead error: {} ERROR CODE {}", (unsigned int)nbytes, (unsigned int)errCode);
}
}
return numRead;
}
int SFileCookieSeek(void *cookie, off64_t *pos, int whence)
{
int swhence;
switch (whence) {
case SEEK_SET:
swhence = DVL_FILE_BEGIN;
break;
case SEEK_CUR:
swhence = DVL_FILE_CURRENT;
break;
case SEEK_END:
swhence = DVL_FILE_END;
break;
default:
return -1;
}
const std::uint64_t spos = SFileSetFilePointer(static_cast<HANDLE>(cookie), *pos, swhence);
if (spos == static_cast<std::uint64_t>(-1)) {
Log("SFileRwSeek error: {}", (unsigned int)SErrGetLastError());
return -1;
}
*pos = static_cast<off64_t>(spos);
return 0;
}
int SFileCookieClose(void *cookie)
{
return SFileCloseFile(static_cast<HANDLE>(cookie)) ? 0 : -1;
}
} // extern "C"
#endif
FILE *FILE_FromStormHandle(HANDLE handle)
{
#ifdef DEVILUTIONX_STORM_FILE_WRAPPER_IMPL_FOPENCOOKIE
cookie_io_functions_t ioFns;
std::memset(&ioFns, 0, sizeof(ioFns));
ioFns.read = &SFileCookieRead;
ioFns.seek = &SFileCookieSeek;
ioFns.close = &SFileCookieClose;
return fopencookie(handle, "rb", ioFns);
#else
#error "unimplemented"
#endif
}
} // namespace devilution
#endif

17
Source/storm/storm_file_wrapper.h

@ -0,0 +1,17 @@
/** A pointer to a Storm file as a `FILE *`. Only available on some platforms. */
#pragma once
#if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__)
#include <cstdio>
#include "miniwin/miniwin.h"
#define DEVILUTIONX_STORM_FILE_WRAPPER_AVAILABLE
#define DEVILUTIONX_STORM_FILE_WRAPPER_IMPL_FOPENCOOKIE
namespace devilution {
FILE *FILE_FromStormHandle(HANDLE handle);
} // namespace devilution
#endif

27
Source/storm/storm_svid.cpp

@ -17,10 +17,11 @@
#include "dx.h" #include "dx.h"
#include "options.h" #include "options.h"
#include "palette.h" #include "palette.h"
#include "storm/storm_file_wrapper.h"
#include "storm/storm.h" #include "storm/storm.h"
#include "utils/display.h" #include "utils/display.h"
#include "utils/sdl_compat.h"
#include "utils/log.hpp" #include "utils/log.hpp"
#include "utils/sdl_compat.h"
namespace devilution { namespace devilution {
namespace { namespace {
@ -39,7 +40,10 @@ smk SVidSMK;
SDL_Color SVidPreviousPalette[256]; SDL_Color SVidPreviousPalette[256];
SDL_Palette *SVidPalette; SDL_Palette *SVidPalette;
SDL_Surface *SVidSurface; SDL_Surface *SVidSurface;
#ifndef DEVILUTIONX_STORM_FILE_WRAPPER_AVAILABLE
std::unique_ptr<uint8_t[]> SVidBuffer; std::unique_ptr<uint8_t[]> SVidBuffer;
#endif
bool IsLandscapeFit(unsigned long srcW, unsigned long srcH, unsigned long dstW, unsigned long dstH) bool IsLandscapeFit(unsigned long srcW, unsigned long srcH, unsigned long dstW, unsigned long dstH)
{ {
@ -129,10 +133,10 @@ bool SVidLoadNextFrame()
} // namespace } // namespace
void SVidPlayBegin(const char *filename, int flags, HANDLE *video) bool SVidPlayBegin(const char *filename, int flags, HANDLE *video)
{ {
if (flags & 0x10000 || flags & 0x20000000) { if (flags & 0x10000 || flags & 0x20000000) {
return; return false;
} }
SVidLoop = false; SVidLoop = false;
@ -146,14 +150,19 @@ void SVidPlayBegin(const char *filename, int flags, HANDLE *video)
//0x200800 // Clear FB //0x200800 // Clear FB
SFileOpenFile(filename, video); SFileOpenFile(filename, video);
#ifdef DEVILUTIONX_STORM_FILE_WRAPPER_AVAILABLE
FILE *file = FILE_FromStormHandle(*video);
SVidSMK = smk_open_filepointer(file, SMK_MODE_DISK);
#else
int bytestoread = SFileGetFileSize(*video, nullptr); int bytestoread = SFileGetFileSize(*video, nullptr);
SVidBuffer = std::make_unique<uint8_t[]>(bytestoread); SVidBuffer = std::make_unique<uint8_t[]>(bytestoread);
SFileReadFile(*video, SVidBuffer.get(), bytestoread, nullptr, nullptr); SFileReadFile(*video, SVidBuffer.get(), bytestoread, nullptr, nullptr);
SFileCloseFile(*video);
*video = nullptr;
SVidSMK = smk_open_memory(SVidBuffer.get(), bytestoread); SVidSMK = smk_open_memory(SVidBuffer.get(), bytestoread);
#endif
if (SVidSMK == nullptr) { if (SVidSMK == nullptr) {
return; return false;
} }
#ifndef NOSOUND #ifndef NOSOUND
@ -236,6 +245,7 @@ void SVidPlayBegin(const char *filename, int flags, HANDLE *video)
SVidFrameEnd = SDL_GetTicks() * 1000 + SVidFrameLength; SVidFrameEnd = SDL_GetTicks() * 1000 + SVidFrameLength;
SDL_FillRect(GetOutputSurface(), nullptr, 0x000000); SDL_FillRect(GetOutputSurface(), nullptr, 0x000000);
return true;
} }
bool SVidPlayContinue() bool SVidPlayContinue()
@ -358,7 +368,9 @@ void SVidPlayEnd(HANDLE video)
if (SVidSMK != nullptr) if (SVidSMK != nullptr)
smk_close(SVidSMK); smk_close(SVidSMK);
#ifndef DEVILUTIONX_STORM_FILE_WRAPPER_AVAILABLE
SVidBuffer = nullptr; SVidBuffer = nullptr;
#endif
SDL_FreePalette(SVidPalette); SDL_FreePalette(SVidPalette);
SVidPalette = nullptr; SVidPalette = nullptr;
@ -366,9 +378,6 @@ void SVidPlayEnd(HANDLE video)
SDL_FreeSurface(SVidSurface); SDL_FreeSurface(SVidSurface);
SVidSurface = nullptr; SVidSurface = nullptr;
SFileCloseFile(video);
video = nullptr;
memcpy(orig_palette, SVidPreviousPalette, sizeof(orig_palette)); memcpy(orig_palette, SVidPreviousPalette, sizeof(orig_palette));
#ifndef USE_SDL1 #ifndef USE_SDL1
if (renderer != nullptr) { if (renderer != nullptr) {

2
Source/storm/storm_svid.h

@ -4,7 +4,7 @@
namespace devilution { namespace devilution {
void SVidPlayBegin(const char *filename, int flags, HANDLE *video); bool SVidPlayBegin(const char *filename, int flags, HANDLE *video);
bool SVidPlayContinue(); bool SVidPlayContinue();
void SVidPlayEnd(HANDLE video); void SVidPlayEnd(HANDLE video);

Loading…
Cancel
Save