#include "../include/Video.hpp" #include #include #include #include #if defined(SFML_SYSTEM_WINDOWS) #ifdef _MSC_VER #include #endif #include #include #elif defined(SFML_SYSTEM_LINUX) || defined(SFML_SYSTEM_FREEBSD) #if defined(SFML_OPENGL_ES) #include #include #else #include #endif #include #define glGetProcAddress glXGetProcAddress #elif defined(SFML_SYSTEM_MACOS) #include #elif defined (SFML_SYSTEM_IOS) #include #include #elif defined (SFML_SYSTEM_ANDROID) #include #include // We're not using OpenGL ES 2+ yet, but we can use the sRGB extension #include #endif using namespace std; namespace dchat { void* getProcAddressMpv(void *funcContext, const char *name) { return (void*)glGetProcAddress((const GLubyte*)name); } void onMpvRedraw(void *rawVideo) { Video *video = (Video*)rawVideo; ++video->redrawCounter; } Video::Video(unsigned int width, unsigned int height, const char *file, bool loop) : redrawCounter(0), context(sf::ContextSettings(), width, height), mpv(nullptr), mpvGl(nullptr), textureBuffer(new sf::Uint8[width * height * 4]) // 4 = red, green, blue and alpha { context.setActive(true); if(!texture.create(width, height)) throw VideoInitializationException("Failed to create texture for video"); texture.setSmooth(true); // mpv_create requires LC_NUMERIC to be set to "C" for some reason, see mpv_create documentation std::setlocale(LC_NUMERIC, "C"); mpv = mpv_create(); if(!mpv) throw VideoInitializationException("Failed to create mpv handle"); if(mpv_initialize(mpv) < 0) throw VideoInitializationException("Failed to initialize mpv"); mpv_opengl_init_params openglInitParams { .get_proc_address = getProcAddressMpv }; mpv_render_param params[] = { { MPV_RENDER_PARAM_API_TYPE, (void*)MPV_RENDER_API_TYPE_OPENGL }, { MPV_RENDER_PARAM_OPENGL_INIT_PARAMS, &openglInitParams }, { (mpv_render_param_type)0, nullptr } }; if(mpv_render_context_create(&mpvGl, mpv, params) < 0) throw VideoInitializationException("Failed to initialize mpv opengl render context"); if(loop) mpv_set_option_string(mpv, "loop", "inf"); mpv_set_option_string(mpv, "hwdec", "auto"); mpv_render_context_set_update_callback(mpvGl, onMpvRedraw, this); renderThread = thread([this, width, height]() { context.setActive(true); while(true) { while(true) { mpv_event *mpvEvent = mpv_wait_event(mpv, 0.0); if(mpvEvent->event_id == MPV_EVENT_NONE) break; else if(mpvEvent->event_id == MPV_EVENT_SHUTDOWN) break; } if(redrawCounter > 0) { --redrawCounter; mpv_opengl_fbo openglFbo { .fbo = 0, .w = (int)width, .h = (int)height }; mpv_render_param params[] = { { MPV_RENDER_PARAM_OPENGL_FBO, &openglFbo }, { (mpv_render_param_type)0, nullptr } }; context.setActive(true); renderMutex.lock(); mpv_render_context_render(mpvGl, params); glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, textureBuffer); texture.update(textureBuffer); sprite.setTexture(texture, true); renderMutex.unlock(); } this_thread::sleep_for(chrono::milliseconds(10)); } }); renderThread.detach(); const char *cmd[] = { "loadfile", file, nullptr }; mpv_command(mpv, cmd); context.setActive(false); } Video::~Video() { lock_guard lock(renderMutex); delete[] textureBuffer; mpv_render_context_free(mpvGl); mpv_destroy(mpv); if(renderThread.joinable()) renderThread.join(); } void Video::setPosition(float x, float y) { sprite.setPosition(x, y); } void Video::draw(sf::RenderWindow &window) { lock_guard lock(renderMutex); window.draw(sprite); } }