#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 alive(true) { 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"); // TODO: Enable this again, but right now it can caused Xorg process to use 100% cpu. //mpv_set_option_string(mpv, "vo", "opengl-cb"); //mpv_set_option_string(mpv, "hwdec", "auto"); if(loop) mpv_set_option_string(mpv, "loop", "inf"); mpvGl = (mpv_opengl_cb_context*)mpv_get_sub_api(mpv, MPV_SUB_API_OPENGL_CB); if(!mpvGl) throw VideoInitializationException("Failed to initialize mpv opengl render context"); mpv_opengl_cb_set_update_callback(mpvGl, onMpvRedraw, this); if(mpv_opengl_cb_init_gl(mpvGl, nullptr, getProcAddressMpv, nullptr) < 0) throw VideoInitializationException("Failed to initialize mpv gl callback func"); renderThread = thread([this, width, height]() { context.setActive(true); while(alive) { 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) return; } if(redrawCounter > 0) { --redrawCounter; context.setActive(true); renderMutex.lock(); //mpv_render_context_render(mpvGl, params); mpv_opengl_cb_draw(mpvGl, 0, width, height); glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, textureBuffer); texture.update(textureBuffer); sprite.setTexture(texture, true); mpv_opengl_cb_report_flip(mpvGl, 0); renderMutex.unlock(); } this_thread::sleep_for(chrono::milliseconds(10)); } }); const char *cmd[] = { "loadfile", file, nullptr }; mpv_command(mpv, cmd); context.setActive(false); } Video::~Video() { alive = false; renderThread.join(); lock_guard lock(renderMutex); context.setActive(true); if(mpvGl) mpv_opengl_cb_set_update_callback(mpvGl, nullptr, nullptr); delete[] textureBuffer; mpv_opengl_cb_uninit_gl(mpvGl); mpv_detach_destroy(mpv); } void Video::setPosition(float x, float y) { sprite.setPosition(x, y); } void Video::draw(sf::RenderWindow &window) { lock_guard lock(renderMutex); window.draw(sprite); } }