[vlc-devel] [PATCH 3/4] libvlc: add sdl_opengl_player sample
Zhao Zhili
quinkblack at foxmail.com
Wed Jul 11 04:18:06 CEST 2018
> On Jul 3, 2018, at 8:38 PM, Pierre Lamot <pierre at videolabs.io> wrote:
>
> this sample shows a simple use of libvlc_video_set_opengl_callbacks
> ---
> doc/libvlc/sdl_opengl_player.cpp | 316 +++++++++++++++++++++++++++++++
> 1 file changed, 316 insertions(+)
> create mode 100644 doc/libvlc/sdl_opengl_player.cpp
>
> diff --git a/doc/libvlc/sdl_opengl_player.cpp b/doc/libvlc/sdl_opengl_player.cpp
> new file mode 100644
> index 0000000000..290b680629
> --- /dev/null
> +++ b/doc/libvlc/sdl_opengl_player.cpp
> @@ -0,0 +1,316 @@
> +//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, create_fbo, destroy_fbo, make_current, get_proc_address, render_fbo, 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 is called to create the surfaces and FBO used by VLC to perform its rendering
> + static void create_fbo(void* data, size_t width, size_t height)
> + {
> + VLCVideo* that = reinterpret_cast<VLCVideo*>(data);
Maybe I'm wrong, I think static_cast is better for this case.
> + 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;
> + }
> + glBindFramebuffer(GL_FRAMEBUFFER, 0);
> + }
> +
> + // This callback is called to release resources created by create_fbo
> + static void destroy_fbo(void* data)
> + {
> + VLCVideo* that = reinterpret_cast<VLCVideo*>(data);
> + glDeleteTextures(3, that->m_tex);
> + glDeleteFramebuffers(3, that->m_fbo);
> + }
> +
> + //This callback is called before and after VLC render the videoframe
> + //This will bind the rendering FBO to the current context
> + static void render_fbo(void* data, bool enter)
> + {
> + VLCVideo* that = reinterpret_cast<VLCVideo*>(data);
> + if (enter) {
> + glBindFramebuffer(GL_FRAMEBUFFER, that->m_fbo[that->m_idx_render]);
> + } else {
> + glBindFramebuffer(GL_FRAMEBUFFER, 0);
> + {
> + std::lock_guard<std::mutex> lock(that->m_text_lock);
> + that->m_updated = true;
> + std::swap(that->m_idx_swap, that->m_idx_render);
> + }
> + }
> + }
> +
> + // This callback is called to set the OpenGL context
> + static bool make_current(void* data, bool current)
> + {
> + VLCVideo* that = reinterpret_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
> + 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_Init(SDL_INIT_VIDEO);
> + 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;
> +}
> --
> 2.17.1
>
> _______________________________________________
> vlc-devel mailing list
> To unsubscribe or modify your subscription options:
> https://mailman.videolan.org/listinfo/vlc-devel
More information about the vlc-devel
mailing list