vlc | branch: master | Pierre Lamot <[email protected]> | Wed Jul 25 15:41:09 2018 +0200| [6b62d9e98b35a2025dba1400ff370bfb96c45f3d] | committer: Thomas Guillem
libvlc: add sdl_opengl_player sample this sample shows a simple use of libvlc_video_set_opengl_callbacks Signed-off-by: Thomas Guillem <[email protected]> > http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=6b62d9e98b35a2025dba1400ff370bfb96c45f3d --- doc/libvlc/sdl_opengl_player.cpp | 333 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 333 insertions(+) diff --git a/doc/libvlc/sdl_opengl_player.cpp b/doc/libvlc/sdl_opengl_player.cpp new file mode 100644 index 0000000000..a9c1a43c5a --- /dev/null +++ b/doc/libvlc/sdl_opengl_player.cpp @@ -0,0 +1,333 @@ +//g++ sdl_opengl_player.cpp $(pkg-config --cflags --libs libvlc sdl2 gl) + +/* Licence WTFPL */ +/* Written by Pierre Lamot */ + +#include <exception> +#include <mutex> +#include <iostream> + +#include <SDL2/SDL.h> +#define GL_GLEXT_PROTOTYPES 1 +#include <SDL2/SDL_opengl.h> +#include <vlc/vlc.h> + +/* + * This program show how to use libvlc_video_set_opengl_callbacks API. + * + * The main idea is to set up libvlc to render into FBO, and to use a + * triple buffer mechanism to share textures between VLC and the rendering + * thread of our application + */ + +// Shader sources +const GLchar* vertexSource = + "attribute vec4 a_position; \n" + "attribute vec2 a_uv; \n" + "varying vec2 v_TexCoordinate; \n" + "void main() \n" + "{ \n" + " v_TexCoordinate = a_uv; \n" + " gl_Position = vec4(a_position.xyz, 1.0); \n" + "} \n"; + +const GLchar* fragmentSource = + "precision mediump float; \n" + "uniform sampler2D u_videotex; \n" + "varying vec2 v_TexCoordinate; \n" + "void main() \n" + "{ \n" + " gl_FragColor = texture2D(u_videotex, v_TexCoordinate); \n" + "} \n"; + +class VLCVideo +{ +public: + VLCVideo(SDL_Window *window): + m_win(window) + { + const char *args[] = { + "--verbose=4" + }; + m_vlc = libvlc_new(sizeof(args) / sizeof(*args), args); + + //VLC opengl context needs to be shared with SDL context + SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); + m_ctx = SDL_GL_CreateContext(window); + } + + ~VLCVideo() + { + stop(); + if (m_vlc) + libvlc_release(m_vlc); + } + + bool playMedia(const char* url) + { + m_media = libvlc_media_new_location (m_vlc, url); + if (m_media == NULL) { + fprintf(stderr, "unable to create media %s", url); + return false; + } + m_mp = libvlc_media_player_new_from_media (m_media); + if (m_mp == NULL) { + fprintf(stderr, "unable to create media player"); + libvlc_media_release(m_media); + return false; + } + // Define the opengl rendering callbacks + libvlc_video_set_opengl_callbacks(m_mp, libvlc_gl_engine_opengl, + setup, cleanup, resize, swap, + make_current, get_proc_address, + this); + + // Play the video + libvlc_media_player_play (m_mp); + + return true; + } + + void stop() + { + if (m_mp) { + libvlc_media_player_release(m_mp); + m_mp = nullptr; + } + if (m_media) { + libvlc_media_release(m_media); + m_media = nullptr; + } + } + + /// return the texture to be displayed + GLuint getVideoFrame(bool* out_updated) + { + std::lock_guard<std::mutex> lock(m_text_lock); + if (out_updated) + *out_updated = m_updated; + if (m_updated) { + std::swap(m_idx_swap, m_idx_display); + m_updated = false; + } + return m_tex[m_idx_display]; + } + + /// this callback will create the surfaces and FBO used by VLC to perform its rendering + static void resize(void* data, unsigned width, unsigned height) + { + VLCVideo* that = static_cast<VLCVideo*>(data); + if (width != that->m_width || height != that->m_height) + cleanup(data); + + glGenTextures(3, that->m_tex); + glGenFramebuffers(3, that->m_fbo); + + for (int i = 0; i < 3; i++) { + glBindTexture(GL_TEXTURE_2D, that->m_tex[i]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glBindFramebuffer(GL_FRAMEBUFFER, that->m_fbo[i]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, that->m_tex[i], 0); + } + glBindTexture(GL_TEXTURE_2D, 0); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + if (status != GL_FRAMEBUFFER_COMPLETE) { + return; + } + + that->m_width = width; + that->m_height = height; + + glBindFramebuffer(GL_FRAMEBUFFER, that->m_fbo[that->m_idx_render]); + } + + // This callback is called during initialisation. + static bool setup(void* data) + { + VLCVideo* that = static_cast<VLCVideo*>(data); + that->m_width = 0; + that->m_height = 0; + return true; + } + + + // This callback is called to release the texture and FBO created in resize + static void cleanup(void* data) + { + VLCVideo* that = static_cast<VLCVideo*>(data); + if (that->m_width == 0 && that->m_height == 0) + return; + + glDeleteTextures(3, that->m_tex); + glDeleteFramebuffers(3, that->m_fbo); + } + + //This callback is called after VLC performs drawing calls + static void swap(void* data) + { + VLCVideo* that = static_cast<VLCVideo*>(data); + std::lock_guard<std::mutex> lock(that->m_text_lock); + that->m_updated = true; + std::swap(that->m_idx_swap, that->m_idx_render); + glBindFramebuffer(GL_FRAMEBUFFER, that->m_fbo[that->m_idx_render]); + } + + // This callback is called to set the OpenGL context + static bool make_current(void* data, bool current) + { + VLCVideo* that = static_cast<VLCVideo*>(data); + if (current) + return SDL_GL_MakeCurrent(that->m_win, that->m_ctx) == 0; + else + return SDL_GL_MakeCurrent(that->m_win, 0) == 0; + } + + // This callback is called by VLC to get OpenGL functions. + static void* get_proc_address(void* /*data*/, const char* current) + { + return SDL_GL_GetProcAddress(current); + } + +private: + //VLC objects + libvlc_instance_t* m_vlc = nullptr; + libvlc_media_player_t* m_mp = nullptr; + libvlc_media_t* m_media = nullptr; + + //FBO data + unsigned m_width = 0; + unsigned m_height = 0; + std::mutex m_text_lock; + GLuint m_tex[3]; + GLuint m_fbo[3]; + size_t m_idx_render = 0; + size_t m_idx_swap = 1; + size_t m_idx_display = 2; + bool m_updated = false; + + //SDL context + SDL_Window* m_win; + SDL_GLContext m_ctx; +}; + +int main(int argc, char** argv) +{ + if (argc != 2) { + fprintf(stderr, "usage: %s <uri>\n", argv[0]); + return 1; + } + + SDL_Window* wnd = SDL_CreateWindow("test", + SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + 640, 480, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN); + + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + SDL_GL_SetSwapInterval(0); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + + SDL_GLContext glc = SDL_GL_CreateContext(wnd); + + VLCVideo video(wnd); + + SDL_Renderer* rdr = SDL_CreateRenderer( + wnd, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE); + + // Create Vertex Array Object + GLuint vao; + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + + // Create a Vertex Buffer Object and copy the vertex data to it + GLuint vbo; + glGenBuffers(1, &vbo); + + //vertex X, vertex Y, UV X, UV Y + GLfloat vertices[] = { + -0.5f, 0.5f, 0.f, 1.f, + -0.5f, -0.5f, 0.f, 0.f, + 0.5f, 0.5f, 1.f, 1.f, + 0.5f, -0.5f, 1.f, 0.f + }; + + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + // Create and compile the vertex shader + GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertexShader, 1, &vertexSource, NULL); + glCompileShader(vertexShader); + + // Create and compile the fragment shader + GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragmentShader, 1, &fragmentSource, NULL); + glCompileShader(fragmentShader); + + // Link the vertex and fragment shader into a shader program + GLuint shaderProgram = glCreateProgram(); + glAttachShader(shaderProgram, vertexShader); + glAttachShader(shaderProgram, fragmentShader); + glLinkProgram(shaderProgram); + glUseProgram(shaderProgram); + + // Specify the layout of the vertex data + GLint posAttrib = glGetAttribLocation(shaderProgram, "a_position"); + glEnableVertexAttribArray(posAttrib); + glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float), 0); + + // Specify the layout of the vertex data + GLint uvAttrib = glGetAttribLocation(shaderProgram, "a_uv"); + glEnableVertexAttribArray(uvAttrib); + glVertexAttribPointer(uvAttrib, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float), (GLvoid*)(2*sizeof(float))); + + // Specify the texture of the video + GLint textUniform = glGetUniformLocation(shaderProgram, "u_videotex"); + glActiveTexture(GL_TEXTURE0); + glUniform1i(textUniform, /*GL_TEXTURE*/0); + + //start playing the video + if (!video.playMedia(argv[1])) { + SDL_DestroyWindow(wnd); + SDL_Quit(); + return 1; + } + + bool updated = false; + bool quit = false; + while(!quit) { + SDL_Event e; + while(SDL_PollEvent(&e)) { + if(e.type == SDL_QUIT) + quit = true; + } + + // Clear the screen to black + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + // Get the current video texture and bind it + GLuint tex = video.getVideoFrame(&updated); + glBindTexture(GL_TEXTURE_2D, tex); + + // Draw the video rectangle + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glBindTexture(GL_TEXTURE_2D, 0); + + SDL_GL_SwapWindow(wnd); + }; + + video.stop(); + + SDL_DestroyWindow(wnd); + SDL_Quit(); + + return 0; +} _______________________________________________ vlc-commits mailing list [email protected] https://mailman.videolan.org/listinfo/vlc-commits
