ARTICLE AD BOX
I am using LibVLC (via the VideoLAN.LibVLC.Windows.3.0.23 NuGet package) on Windows 10 to reproduce a video (MiVideo.mp4, 1920x1080):
#include <glad/glad.h> // OpenGl #include <GLFW/glfw3.h> // GLFW Window #include <vlc/vlc.h> // libCLV #include <cassert> // assert #include <vector> // std::vector // Forward declarations of LibVLC callback functions void* vlcLockCallback(void* opaque, void** planes); void vlcDisplayCallback(void* opaque, void* picture); class VLC_Data { public: libvlc_instance_t* mLibvlc = nullptr; libvlc_media_t* mMedia = nullptr; libvlc_media_player_t* mMediaPlayer = nullptr; unsigned int mTextureHandle = 0; // texture for quad to draw every video-frame onto screen std::vector<uint8_t> mPixelBuffer = {}; // for getting pixels from libVLC and storing them in texture in every video-frame bool mReadyToDisplay = false; // false when lock, true when unlock, when ready submit data into texture VLC_Data() { // Generate empty texture, the one that will be updated with every video frame when displaying the video glGenTextures(1, &mTextureHandle); glBindTexture(GL_TEXTURE_2D, mTextureHandle); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1920, 1080, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); mPixelBuffer.resize(1920 * 1080 * 4); // pixels times 4, cause it's RGBA // Create libVLC instance and load "MiVideo.mp4" mLibvlc = libvlc_new(0, NULL); mMedia = libvlc_media_new_path(mLibvlc, "MiVideo.mp4"); mMediaPlayer = libvlc_media_player_new_from_media(mMedia); libvlc_media_release(mMedia); // Set video callbacks (lock and display functions of VLC) libvlc_video_set_format(mMediaPlayer, "RGBA", 1920, 1080, 1920 * 4); libvlc_video_set_callbacks(mMediaPlayer, vlcLockCallback, nullptr, vlcDisplayCallback, this); } void Play() { assert(libvlc_media_player_play(mMediaPlayer) != -1); } ~VLC_Data() { libvlc_media_player_release(mMediaPlayer); libvlc_release(mLibvlc); glDeleteTextures(1, &mTextureHandle); } }; // VLC calls this when it's about to decode a frame void* vlcLockCallback(void* opaque, void** planes) { VLC_Data* vlcData = static_cast<VLC_Data*>(opaque); *planes = vlcData->mPixelBuffer.data(); return nullptr; } // VLC calls this when the frame is ready to be shown void vlcDisplayCallback(void* opaque, void* picture) { VLC_Data* vlcData = static_cast<VLC_Data*>(opaque); if (vlcData->mReadyToDisplay) { // Submit video-frame pixels to texture glBindTexture(GL_TEXTURE_2D, vlcData->mTextureHandle); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1920, 1080, GL_RGBA, GL_UNSIGNED_BYTE, vlcData->mPixelBuffer.data()); vlcData->mReadyToDisplay = false; } } class QuadModel { public: size_t mIndexCount = 0; unsigned int mVAO = 0; unsigned int mVBO = 0; unsigned int mEBO = 0; QuadModel() { // Create buffers glGenVertexArrays(1, &mVAO); glGenBuffers(1, &mVBO); glGenBuffers(1, &mEBO); // Vertex data for quad std::vector<float> vertices = { // positions // texCoords -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, -1.0f, 0.0f, 1.0f, 1.0f }; std::vector<unsigned int> indices = { 0, 1, 2, 1, 3, 2 }; mIndexCount = indices.size(); // Bind VAO glBindVertexArray(mVAO); // Bind VBO and submit vertex data glBindBuffer(GL_ARRAY_BUFFER, mVBO); glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_STATIC_DRAW); // Bind EBO and submit index data glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mEBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW); // Set vertex attributes glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), reinterpret_cast<void*>(0 * sizeof(float))); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), reinterpret_cast<void*>(3 * sizeof(float))); // Unbind glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); assert(glGetError() == 0); } void Draw() { // Bind VAO glBindVertexArray(mVAO); // Draw quad glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glDrawElements(GL_TRIANGLES, mIndexCount, GL_UNSIGNED_INT, 0); // Unbind VAO glBindVertexArray(0); assert(glGetError() == 0); } ~QuadModel() { glDeleteBuffers(1, &mVBO); glDeleteBuffers(1, &mEBO); glDeleteVertexArrays(1, &mVAO); } }; class Shader { public: unsigned int mHandle; Shader() { char const* c_vertex_shader = R"( #version 330 core layout (location = 0) in vec3 v_position; layout (location = 1) in vec2 v_texCoord; out vec2 f_texCoord; void main() { f_texCoord = v_texCoord; gl_Position = vec4(v_position, 1.0f); } )"; char const* c_fragment_shader = R"( #version 330 core in vec2 f_texCoord; out vec4 FragColor; uniform sampler2D diffuseTextureID; void main() { FragColor = texture(diffuseTextureID, f_texCoord); } )"; // Compile vertex shader GLuint vertexShaderHandle = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertexShaderHandle, 1, &c_vertex_shader, NULL); glCompileShader(vertexShaderHandle); GLint success = 0; glGetShaderiv(vertexShaderHandle, GL_COMPILE_STATUS, &success); assert(success); // Compile fragment shader GLuint fragmentShaderHandle = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragmentShaderHandle, 1, &c_fragment_shader, NULL); glCompileShader(fragmentShaderHandle); glGetShaderiv(fragmentShaderHandle, GL_COMPILE_STATUS, &success); assert(success); // Create shader program mHandle = glCreateProgram(); glAttachShader(mHandle, vertexShaderHandle); glAttachShader(mHandle, fragmentShaderHandle); glLinkProgram(mHandle); glGetProgramiv(mHandle, GL_LINK_STATUS, &success); assert(success); glDeleteShader(vertexShaderHandle); glDeleteShader(fragmentShaderHandle); // Use shader program glUseProgram(mHandle); } }; class Window { public: GLFWwindow* GLFWwindow = nullptr; Window() { // Initialize and configure glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); GLFWwindow = glfwCreateWindow(1920, 1080, "libVLC: Minimal Reproducible Example", NULL, NULL); glfwMakeContextCurrent(GLFWwindow); gladLoadGLLoader(reinterpret_cast<GLADloadproc>(glfwGetProcAddress)); } void ClearBuffers() { glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClearDepth(1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } void SwapBufferAndPollEvents() { glfwSwapBuffers(GLFWwindow); glfwPollEvents(); } ~Window() { glfwTerminate(); glfwDestroyWindow(GLFWwindow); } }; int main() { Window window; // create window QuadModel quadModel; // create quad model for drawing onto screen Shader shader; // create and activate a simple shader to draw quad with a texture VLC_Data vlcData; // create texture, libVLC instance and load "MiVideo.mp4" // Set texture (updated by vlcData) to shader glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, vlcData.mTextureHandle); // Play "MiVideo.mp4" vlcData.Play(); // Loop while (glfwGetKey(window.GLFWwindow, GLFW_KEY_ESCAPE) != GLFW_PRESS) { window.ClearBuffers(); quadModel.Draw(); window.SwapBufferAndPollEvents(); } // Resources are deleted in Window, QuadModel and VLC_Data destructors return 0; }...and I am getting these errors multiple times:
main filter error: Failed to create video converter main vout display error: Failed to set on topAnd of course, the video textures are not displayed. I am trying to reproduce a video of width 1920 and height 1080 pixels values hardcoded. I've tried reproducing the video in mp4 and mkv formats, but I get the same results in both. After calling libvlc_media_player_play (within VLC_Data::Play) the errors appear, and I don't know how to solve this.
Any help?
