Skip to content

Instantly share code, notes, and snippets.

@rvega
Created April 10, 2018 01:59
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save rvega/18d84addc474a4175d3b72c091d7aaae to your computer and use it in GitHub Desktop.
Save rvega/18d84addc474a4175d3b72c091d7aaae to your computer and use it in GitHub Desktop.
A simple video player widget with nanogui and libvlc
#include <nanogui/screen.h>
#include <nanogui/window.h>
#include <nanogui/glcanvas.h>
#include <nanogui/layout.h>
#include <nanogui/opengl.h>
#include <vlc/vlc.h>
#include <iostream>
#include <mutex>
#include <cstdint>
#include <thread>
class VideoCanvas : public nanogui::GLCanvas {
protected:
nanogui::GLShader mTexShader;
int mWidth = 0;
int mHeight = 0;
GLuint mTex;
std::mutex mDataMutex;
char *mData = nullptr;
bool needUpdate;
libvlc_instance_t *vlc;
libvlc_media_player_t *mediaPlayer;
public:
VideoCanvas(nanogui::Widget *parent, int width, int height)
: nanogui::GLCanvas(parent)
, mWidth(width)
, mHeight(height) {
mData = new char[480*320*3];
needUpdate = false;
initGL();
initVLC();
}
~VideoCanvas() {
delete[] mData;
glDeleteTextures(1, &mTex);
}
void initVLC() {
const char *argv[] = {
"--no-xlib",
"--no-video-title-show"
};
int argc = sizeof(argv) / sizeof(*argv);
vlc = libvlc_new(argc, argv);
libvlc_media_t *m;
m = libvlc_media_new_path(vlc, "/home/rvg/Projects/Explora/1.m4v");
mediaPlayer = libvlc_media_player_new_from_media(m);
libvlc_media_release(m);
libvlc_video_set_callbacks(mediaPlayer, videoLockCallBack, videoUnlockCallback, NULL, this);
libvlc_video_set_format(mediaPlayer, "RV24", 480, 320, 480*3);
libvlc_media_player_play(mediaPlayer);
}
void initGL() {
// create the OpenGL texture
glGenTextures(1, &mTex);
glBindTexture(GL_TEXTURE_2D, mTex);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// initialize the video feed shader
// create shader that will use to display textures
mTexShader.init(
/* registered name */
"video_shader",
/* vertex shader */
"#version 330\n"
"// inputs: should just be the vertices and uv's for a rectangle\n"
"in vec2 position;\n"
"in vec2 texcoord;\n"
"// outputs: interpolated texture coordinates for fragment shader\n"
"out vec2 pass_texcoord;\n"
"void main() {\n"
" pass_texcoord = texcoord;\n"
" gl_Position = vec4(position.xy, 0.0f, 1.0f);\n"
"}\n",
/* fragment shader */
"#version 330\n"
"in vec2 pass_texcoord;// interpolated texture coordinate\n"
"out vec4 outColor; // output color sampled from texture\n"
"uniform sampler2D passTex;// sampler to read the texture\n"
"void main() {\n"
" outColor = texture(passTex, pass_texcoord);\n"
"}\n"
);
nanogui::MatrixXu indices(3, 2);
indices.col(0) << 0, 1, 2;
indices.col(1) << 2, 3, 1;
nanogui::MatrixXf positions(2, 4);
positions.col(0) << -1.0f, 1.0f;
positions.col(1) << 1.0f, 1.0f;
positions.col(2) << -1.0f, -1.0f;
positions.col(3) << 1.0f, -1.0f;
nanogui::MatrixXf texcoords(2, 4);
texcoords.col(0) << 0.0f, 0.0f;
texcoords.col(1) << 1.0f, 0.0f;
texcoords.col(2) << 0.0f, 1.0f;
texcoords.col(3) << 1.0f, 1.0f;
mTexShader.bind();
mTexShader.uploadIndices(indices);
mTexShader.uploadAttrib("position", positions);
mTexShader.uploadAttrib("texcoord", texcoords);
}
void drawGL() override {
mTexShader.bind();
glActiveTexture(1);
glUniform1i(mTexShader.uniform("passTex", true), 1 - GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mTex);
{
std::lock_guard<std::mutex> data_lock(mDataMutex);
// upload the image data (only needed when new data comes in from newFrame)
if(needUpdate) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, mWidth, mHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, mData);
needUpdate = false;
}
}
mTexShader.drawIndexed(GL_TRIANGLES, 0, 2);
}
static void *videoLockCallBack(void *object, void **planes) {
VideoCanvas* c = (VideoCanvas *)object;
c->mDataMutex.lock();
planes[0] = c->mData;
return NULL;
}
static void videoUnlockCallback(void *object, void *picture, void * const *planes) {
VideoCanvas* c = (VideoCanvas *)object;
c->needUpdate = true;
c->mDataMutex.unlock();
}
};
class MyScreen : public nanogui::Screen {
public:
MyScreen() : nanogui::Screen({600, 600}, "Texture Testing") {
auto *window = new nanogui::Window(this, "Video Data");
window->setLayout(new nanogui::GroupLayout());
mVideoCanvas = new VideoCanvas(window, 480, 320);
mVideoCanvas->setSize({480, 320});
}
~MyScreen() { }
protected:
VideoCanvas *mVideoCanvas = nullptr;
};
int main(int argc, const char **argv) {
nanogui::init();
auto *screen = new MyScreen();
screen->performLayout();
screen->setVisible(true);
nanogui::mainloop();
nanogui::shutdown();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment