mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-24 00:00:13 +00:00
Play looping MP4 behind auth screen
This commit is contained in:
parent
77a21609a8
commit
8c3aa6542e
6 changed files with 352 additions and 0 deletions
|
|
@ -21,6 +21,8 @@ find_package(GLEW REQUIRED)
|
|||
find_package(OpenSSL REQUIRED)
|
||||
find_package(Threads REQUIRED)
|
||||
find_package(SQLite3 REQUIRED)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(FFMPEG REQUIRED libavformat libavcodec libswscale libavutil)
|
||||
|
||||
# GLM (header-only math library)
|
||||
find_package(glm QUIET)
|
||||
|
|
@ -139,6 +141,7 @@ set(WOWEE_SOURCES
|
|||
src/rendering/world_map.cpp
|
||||
src/rendering/swim_effects.cpp
|
||||
src/rendering/loading_screen.cpp
|
||||
src/rendering/video_player.cpp
|
||||
|
||||
# UI
|
||||
src/ui/ui_manager.cpp
|
||||
|
|
@ -226,6 +229,7 @@ set(WOWEE_HEADERS
|
|||
include/rendering/character_preview.hpp
|
||||
include/rendering/wmo_renderer.hpp
|
||||
include/rendering/loading_screen.hpp
|
||||
include/rendering/video_player.hpp
|
||||
|
||||
include/ui/ui_manager.hpp
|
||||
include/ui/auth_screen.hpp
|
||||
|
|
@ -245,6 +249,7 @@ target_include_directories(wowee PRIVATE
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/extern
|
||||
${FFMPEG_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
# Link libraries
|
||||
|
|
@ -265,6 +270,11 @@ else()
|
|||
target_link_libraries(wowee PRIVATE ${SQLite3_LIBRARIES})
|
||||
endif()
|
||||
|
||||
target_link_libraries(wowee PRIVATE ${FFMPEG_LIBRARIES})
|
||||
if (FFMPEG_LIBRARY_DIRS)
|
||||
target_link_directories(wowee PRIVATE ${FFMPEG_LIBRARY_DIRS})
|
||||
endif()
|
||||
|
||||
# Platform-specific libraries
|
||||
if(WIN32)
|
||||
target_link_libraries(wowee PRIVATE ws2_32)
|
||||
|
|
|
|||
BIN
assets/startscreen.mp4
Executable file
BIN
assets/startscreen.mp4
Executable file
Binary file not shown.
51
include/rendering/video_player.hpp
Normal file
51
include/rendering/video_player.hpp
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
typedef unsigned int GLuint;
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
class VideoPlayer {
|
||||
public:
|
||||
VideoPlayer();
|
||||
~VideoPlayer();
|
||||
|
||||
bool open(const std::string& path);
|
||||
void update(float deltaTime);
|
||||
void close();
|
||||
|
||||
bool isReady() const { return textureReady; }
|
||||
GLuint getTextureId() const { return textureId; }
|
||||
int getWidth() const { return width; }
|
||||
int getHeight() const { return height; }
|
||||
|
||||
private:
|
||||
bool decodeNextFrame();
|
||||
void uploadFrame();
|
||||
|
||||
void* formatCtx = nullptr;
|
||||
void* codecCtx = nullptr;
|
||||
void* frame = nullptr;
|
||||
void* rgbFrame = nullptr;
|
||||
void* packet = nullptr;
|
||||
void* swsCtx = nullptr;
|
||||
|
||||
int videoStreamIndex = -1;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
double frameTime = 1.0 / 30.0;
|
||||
double accumulator = 0.0;
|
||||
bool eof = false;
|
||||
|
||||
GLuint textureId = 0;
|
||||
bool textureReady = false;
|
||||
std::string sourcePath;
|
||||
std::vector<uint8_t> rgbBuffer;
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "auth/auth_handler.hpp"
|
||||
#include "rendering/video_player.hpp"
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
|
|
@ -83,6 +84,10 @@ private:
|
|||
void loadLoginInfo();
|
||||
static std::string getConfigPath();
|
||||
bool loginInfoLoaded = false;
|
||||
|
||||
// Background video
|
||||
bool videoInitAttempted = false;
|
||||
rendering::VideoPlayer backgroundVideo;
|
||||
};
|
||||
|
||||
}} // namespace wowee::ui
|
||||
|
|
|
|||
253
src/rendering/video_player.cpp
Normal file
253
src/rendering/video_player.cpp
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
#include "rendering/video_player.hpp"
|
||||
#include "core/logger.hpp"
|
||||
#include <GL/glew.h>
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libswscale/swscale.h>
|
||||
#include <libavutil/imgutils.h>
|
||||
}
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
VideoPlayer::VideoPlayer() = default;
|
||||
|
||||
VideoPlayer::~VideoPlayer() {
|
||||
close();
|
||||
}
|
||||
|
||||
bool VideoPlayer::open(const std::string& path) {
|
||||
if (!path.empty() && sourcePath == path && formatCtx) return true;
|
||||
close();
|
||||
|
||||
sourcePath = path;
|
||||
AVFormatContext* fmt = nullptr;
|
||||
if (avformat_open_input(&fmt, path.c_str(), nullptr, nullptr) != 0) {
|
||||
LOG_WARNING("VideoPlayer: failed to open ", path);
|
||||
return false;
|
||||
}
|
||||
if (avformat_find_stream_info(fmt, nullptr) < 0) {
|
||||
LOG_WARNING("VideoPlayer: failed to read stream info for ", path);
|
||||
avformat_close_input(&fmt);
|
||||
return false;
|
||||
}
|
||||
|
||||
int streamIndex = -1;
|
||||
for (unsigned int i = 0; i < fmt->nb_streams; i++) {
|
||||
if (fmt->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||
streamIndex = static_cast<int>(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (streamIndex < 0) {
|
||||
LOG_WARNING("VideoPlayer: no video stream in ", path);
|
||||
avformat_close_input(&fmt);
|
||||
return false;
|
||||
}
|
||||
|
||||
AVCodecParameters* codecpar = fmt->streams[streamIndex]->codecpar;
|
||||
const AVCodec* codec = avcodec_find_decoder(codecpar->codec_id);
|
||||
if (!codec) {
|
||||
LOG_WARNING("VideoPlayer: unsupported codec for ", path);
|
||||
avformat_close_input(&fmt);
|
||||
return false;
|
||||
}
|
||||
|
||||
AVCodecContext* ctx = avcodec_alloc_context3(codec);
|
||||
if (!ctx) {
|
||||
avformat_close_input(&fmt);
|
||||
return false;
|
||||
}
|
||||
if (avcodec_parameters_to_context(ctx, codecpar) < 0) {
|
||||
avcodec_free_context(&ctx);
|
||||
avformat_close_input(&fmt);
|
||||
return false;
|
||||
}
|
||||
if (avcodec_open2(ctx, codec, nullptr) < 0) {
|
||||
avcodec_free_context(&ctx);
|
||||
avformat_close_input(&fmt);
|
||||
return false;
|
||||
}
|
||||
|
||||
AVFrame* f = av_frame_alloc();
|
||||
AVFrame* rgb = av_frame_alloc();
|
||||
AVPacket* pkt = av_packet_alloc();
|
||||
if (!f || !rgb || !pkt) {
|
||||
if (pkt) av_packet_free(&pkt);
|
||||
if (rgb) av_frame_free(&rgb);
|
||||
if (f) av_frame_free(&f);
|
||||
avcodec_free_context(&ctx);
|
||||
avformat_close_input(&fmt);
|
||||
return false;
|
||||
}
|
||||
|
||||
width = ctx->width;
|
||||
height = ctx->height;
|
||||
if (width <= 0 || height <= 0) {
|
||||
av_packet_free(&pkt);
|
||||
av_frame_free(&rgb);
|
||||
av_frame_free(&f);
|
||||
avcodec_free_context(&ctx);
|
||||
avformat_close_input(&fmt);
|
||||
return false;
|
||||
}
|
||||
|
||||
int bufferSize = av_image_get_buffer_size(AV_PIX_FMT_RGB24, width, height, 1);
|
||||
rgbBuffer.resize(static_cast<size_t>(bufferSize));
|
||||
av_image_fill_arrays(rgb->data, rgb->linesize,
|
||||
rgbBuffer.data(), AV_PIX_FMT_RGB24, width, height, 1);
|
||||
|
||||
SwsContext* sws = sws_getContext(width, height, ctx->pix_fmt,
|
||||
width, height, AV_PIX_FMT_RGB24,
|
||||
SWS_BILINEAR, nullptr, nullptr, nullptr);
|
||||
if (!sws) {
|
||||
av_packet_free(&pkt);
|
||||
av_frame_free(&rgb);
|
||||
av_frame_free(&f);
|
||||
avcodec_free_context(&ctx);
|
||||
avformat_close_input(&fmt);
|
||||
return false;
|
||||
}
|
||||
|
||||
AVRational fr = fmt->streams[streamIndex]->avg_frame_rate;
|
||||
if (fr.num <= 0 || fr.den <= 0) {
|
||||
fr = fmt->streams[streamIndex]->r_frame_rate;
|
||||
}
|
||||
double fps = (fr.num > 0 && fr.den > 0) ? static_cast<double>(fr.num) / fr.den : 30.0;
|
||||
if (fps <= 0.0) fps = 30.0;
|
||||
frameTime = 1.0 / fps;
|
||||
accumulator = 0.0;
|
||||
eof = false;
|
||||
|
||||
formatCtx = fmt;
|
||||
codecCtx = ctx;
|
||||
frame = f;
|
||||
rgbFrame = rgb;
|
||||
packet = pkt;
|
||||
swsCtx = sws;
|
||||
videoStreamIndex = streamIndex;
|
||||
|
||||
textureReady = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void VideoPlayer::close() {
|
||||
if (textureId) {
|
||||
glDeleteTextures(1, &textureId);
|
||||
textureId = 0;
|
||||
}
|
||||
textureReady = false;
|
||||
|
||||
if (packet) {
|
||||
av_packet_free(reinterpret_cast<AVPacket**>(&packet));
|
||||
packet = nullptr;
|
||||
}
|
||||
if (rgbFrame) {
|
||||
av_frame_free(reinterpret_cast<AVFrame**>(&rgbFrame));
|
||||
rgbFrame = nullptr;
|
||||
}
|
||||
if (frame) {
|
||||
av_frame_free(reinterpret_cast<AVFrame**>(&frame));
|
||||
frame = nullptr;
|
||||
}
|
||||
if (codecCtx) {
|
||||
avcodec_free_context(reinterpret_cast<AVCodecContext**>(&codecCtx));
|
||||
codecCtx = nullptr;
|
||||
}
|
||||
if (formatCtx) {
|
||||
avformat_close_input(reinterpret_cast<AVFormatContext**>(&formatCtx));
|
||||
formatCtx = nullptr;
|
||||
}
|
||||
if (swsCtx) {
|
||||
sws_freeContext(reinterpret_cast<SwsContext*>(swsCtx));
|
||||
swsCtx = nullptr;
|
||||
}
|
||||
videoStreamIndex = -1;
|
||||
width = 0;
|
||||
height = 0;
|
||||
rgbBuffer.clear();
|
||||
}
|
||||
|
||||
void VideoPlayer::update(float deltaTime) {
|
||||
if (!formatCtx || !codecCtx) return;
|
||||
accumulator += deltaTime;
|
||||
while (accumulator >= frameTime) {
|
||||
if (!decodeNextFrame()) break;
|
||||
accumulator -= frameTime;
|
||||
}
|
||||
}
|
||||
|
||||
bool VideoPlayer::decodeNextFrame() {
|
||||
AVFormatContext* fmt = reinterpret_cast<AVFormatContext*>(formatCtx);
|
||||
AVCodecContext* ctx = reinterpret_cast<AVCodecContext*>(codecCtx);
|
||||
AVFrame* f = reinterpret_cast<AVFrame*>(frame);
|
||||
AVFrame* rgb = reinterpret_cast<AVFrame*>(rgbFrame);
|
||||
AVPacket* pkt = reinterpret_cast<AVPacket*>(packet);
|
||||
SwsContext* sws = reinterpret_cast<SwsContext*>(swsCtx);
|
||||
|
||||
while (true) {
|
||||
int ret = av_read_frame(fmt, pkt);
|
||||
if (ret < 0) {
|
||||
if (av_seek_frame(fmt, videoStreamIndex, 0, AVSEEK_FLAG_BACKWARD) >= 0) {
|
||||
avcodec_flush_buffers(ctx);
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pkt->stream_index != videoStreamIndex) {
|
||||
av_packet_unref(pkt);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (avcodec_send_packet(ctx, pkt) < 0) {
|
||||
av_packet_unref(pkt);
|
||||
continue;
|
||||
}
|
||||
av_packet_unref(pkt);
|
||||
|
||||
ret = avcodec_receive_frame(ctx, f);
|
||||
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
|
||||
continue;
|
||||
}
|
||||
if (ret < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
sws_scale(sws,
|
||||
f->data, f->linesize,
|
||||
0, ctx->height,
|
||||
rgb->data, rgb->linesize);
|
||||
|
||||
uploadFrame();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void VideoPlayer::uploadFrame() {
|
||||
if (width <= 0 || height <= 0) return;
|
||||
if (!textureId) {
|
||||
glGenTextures(1, &textureId);
|
||||
glBindTexture(GL_TEXTURE_2D, textureId);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0,
|
||||
GL_RGB, GL_UNSIGNED_BYTE, rgbBuffer.data());
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
textureReady = true;
|
||||
return;
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, textureId);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height,
|
||||
GL_RGB, GL_UNSIGNED_BYTE, rgbBuffer.data());
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
textureReady = true;
|
||||
}
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
|
|
@ -38,6 +38,39 @@ void AuthScreen::render(auth::AuthHandler& authHandler) {
|
|||
loginInfoLoaded = true;
|
||||
}
|
||||
|
||||
if (!videoInitAttempted) {
|
||||
videoInitAttempted = true;
|
||||
backgroundVideo.open("assets/startscreen.mp4");
|
||||
}
|
||||
backgroundVideo.update(ImGui::GetIO().DeltaTime);
|
||||
if (backgroundVideo.isReady()) {
|
||||
ImVec2 screen = ImGui::GetIO().DisplaySize;
|
||||
float screenW = screen.x;
|
||||
float screenH = screen.y;
|
||||
float videoW = static_cast<float>(backgroundVideo.getWidth());
|
||||
float videoH = static_cast<float>(backgroundVideo.getHeight());
|
||||
if (videoW > 0.0f && videoH > 0.0f) {
|
||||
float screenAspect = screenW / screenH;
|
||||
float videoAspect = videoW / videoH;
|
||||
ImVec2 uv0(0.0f, 0.0f);
|
||||
ImVec2 uv1(1.0f, 1.0f);
|
||||
if (videoAspect > screenAspect) {
|
||||
float scale = screenAspect / videoAspect;
|
||||
float crop = (1.0f - scale) * 0.5f;
|
||||
uv0.x = crop;
|
||||
uv1.x = 1.0f - crop;
|
||||
} else if (videoAspect < screenAspect) {
|
||||
float scale = videoAspect / screenAspect;
|
||||
float crop = (1.0f - scale) * 0.5f;
|
||||
uv0.y = crop;
|
||||
uv1.y = 1.0f - crop;
|
||||
}
|
||||
ImDrawList* bg = ImGui::GetBackgroundDrawList();
|
||||
bg->AddImage(static_cast<ImTextureID>(static_cast<uintptr_t>(backgroundVideo.getTextureId())),
|
||||
ImVec2(0, 0), ImVec2(screenW, screenH), uv0, uv1);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver);
|
||||
ImGui::Begin("WoW 3.3.5a Authentication", nullptr, ImGuiWindowFlags_NoCollapse);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue