Skip to content

Instantly share code, notes, and snippets.

@roxlu
Created December 9, 2013 13:37
Show Gist options
  • Save roxlu/7872352 to your computer and use it in GitHub Desktop.
Save roxlu/7872352 to your computer and use it in GitHub Desktop.
Mac 10.9 using YUV input with opengl
#ifndef PHOTOBOOTH_SHADERS_H
#define PHOTOBOOTH_SHADERS_H
#include <opengl/Headers.h>
#include <math/Math.h>
// --------------------------------------------------
static const char* P_VS = ""
"#version 150\n"
"uniform mat4 u_pm;"
"in vec4 a_pos;"
"void main() {"
" gl_Position = u_pm * a_pos;"
"}"
"";
static const char* P_FS = ""
"#version 150\n"
"out vec4 fragcolor; "
"void main() {"
" fragcolor = vec4(0.5, 0.3, 0.2, 1.0); "
"}"
"";
static const char* PT_VS = ""
"#version 150\n"
"uniform mat4 u_pm;"
"in vec4 a_pos;"
"in vec2 a_tex;"
"out vec2 v_tex;"
"void main() {"
" gl_Position = u_pm * a_pos;"
" v_tex = a_tex;"
"}"
"";
static const char* PT_FS = ""
"#version 150\n"
"uniform sampler2D u_tex;"
"out vec4 fragcolor;"
"in vec2 v_tex;"
"void main() {"
" fragcolor = vec4(0.5, 1.0, 0.2, 1.0); "
"}"
"";
static const char* YUV_FS = ""
"#version 150\n"
"uniform sampler2D u_tex;"
"in vec2 v_tex;"
"out vec4 fragcolor; "
""
"const vec3 R_cf = vec3(1.164383, 0.000000, 1.596027);"
"const vec3 G_cf = vec3(1.164383, -0.391762, -0.812968);"
"const vec3 B_cf = vec3(1.164383, 2.017232, 0.000000);"
"const vec3 offset = vec3(-0.0625, -0.5, -0.5);"
"void main() {"
" vec3 tc = texture(u_tex, v_tex).rgb;"
" vec3 yuv = vec3(tc.g, tc.b, tc.r);"
" yuv += offset;"
" fragcolor.r = dot(yuv, R_cf);"
" fragcolor.g = dot(yuv, G_cf);"
" fragcolor.b = dot(yuv, B_cf);"
" fragcolor.a = 1.0;"
"}"
"";
// --------------------------------------------------
class PhotoBooth;
class Shaders {
public:
Shaders(PhotoBooth& booth);
~Shaders();
bool setup();
public:
PhotoBooth& booth;
/* VertexP shader */
GLuint vert_p;
GLuint frag_p_debug;
GLuint prog_p_debug;
/* VertexPT shader */
GLuint vert_pt;
GLuint frag_pt;
GLuint prog_pt;
/* YUYV shader */
GLuint frag_yuv;
GLuint prog_yuv;
Mat4 ortho_matrix;
};
#endif
#include <assert.h>
#include <photobooth/Webcam.h>
#include <photobooth/PhotoBooth.h>
void frame_callback(void* pixels, size_t nbytes, void* user) {
Webcam* c = static_cast<Webcam*>(user);
assert(c->yuv_pixels);
uv_mutex_lock(&c->yuv_mutex);
memcpy((char*)c->yuv_pixels, (char*)pixels, nbytes);
c->has_new_frame = true;
uv_mutex_unlock(&c->yuv_mutex);
}
// ---------------------------------------------------
Webcam::Webcam(PhotoBooth& booth)
:booth(booth)
,cam_vao(0)
,cam_vbo(0)
,cam_tex(0)
,has_new_frame(false)
,yuv_pixels(NULL)
{
stop();
uv_mutex_init(&yuv_mutex);
}
Webcam::~Webcam() {
printf("@TODO: Stop capture!.\n");
uv_mutex_destroy(&yuv_mutex);
}
bool Webcam::setup() {
// setup video capture.
VideoCaptureSettings cfg;
cfg.width = 1280;
cfg.height = 720;
cfg.in_pixel_format = VIDEOCAPTURE_FMT_UYVY422;
cfg.fps = 30.0f;
if(!cap.openDevice(0, cfg, frame_callback, this)) {
printf("Cannot setup the webcam.\n");
return false;
}
// create rectangle to draw webcam in.
glGenVertexArrays(1, &cam_vao);
glBindVertexArray(cam_vao);
glGenBuffers(1, &cam_vbo);
glBindBuffer(GL_ARRAY_BUFFER, cam_vbo);
glEnableVertexAttribArray(0); // pos;
glEnableVertexAttribArray(1); // tex
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexPT), (GLvoid*)0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(VertexPT), (GLvoid*)12);
Vertices<VertexPT> verts;
verts.push_back(VertexPT(Vec3(0.0f, 0.0f, 0.0f), Vec2(0.0f, 1.0f))); // bottom left
verts.push_back(VertexPT(Vec3(0.0f, cfg.height, 0.0f), Vec2(0.0f, 0.0f))); // top left
verts.push_back(VertexPT(Vec3(cfg.width, 0.0f, 0.0f), Vec2(1.0f, 1.0f))); // bottom right
verts.push_back(VertexPT(Vec3(cfg.width, cfg.height, 0.0f), Vec2(1.0f, 0.0f))); // top right
glBufferData(GL_ARRAY_BUFFER, verts.getNumBytes(), verts.getPtr(), GL_STATIC_DRAW);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// Webcam texture
glGenTextures(1, &tex_yuv);
glBindTexture(GL_TEXTURE_2D, tex_yuv);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, cfg.width, cfg.height, 0,
GL_RGB_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, 0);
rx_set_texture_parameters();
glBindTexture(GL_TEXTURE_2D, 0);
yuv_pixels = new unsigned char[cfg.width * cfg.height * 2];
if(!yuv_pixels) {
printf("Cannot allocate yuv pixel container.\n");
return false;
}
return true;
}
void Webcam::update() {
uv_mutex_lock(&yuv_mutex);
{
if(has_new_frame) {
glBindTexture(GL_TEXTURE_2D, tex_yuv);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, cap.getWidth(), cap.getHeight(),
GL_RGB_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, yuv_pixels);
has_new_frame = false;
}
}
uv_mutex_unlock(&yuv_mutex);
cap.update();
}
void Webcam::draw() {
glBindVertexArray(cam_vao);
glUseProgram(booth.shaders.prog_yuv);
glUniformMatrix4fv(glGetUniformLocation(booth.shaders.prog_yuv, "u_pm"), 1, GL_FALSE, booth.shaders.ortho_matrix.getPtr());
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex_yuv);
glUniform1i(glGetUniformLocation(booth.shaders.prog_yuv, "u_tex"), 0);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
bool Webcam::start() {
if(!cap.startCapture()) {
printf("Cannot start the capture.\n");
return false;
}
return true;
}
bool Webcam::stop() {
if(!cap.stopCapture()) {
printf("Cannot stop the capture.\n");
return false;
}
return true;
}
#ifndef PHOTOBOOTH_WEBCAM_H
#define PHOTOBOOTH_WEBCAM_H
#include <videocapture/VideoCapture.h>
#include <opengl/Includes.h>
#include <uv.h>
void frame_callback(void* pixels, size_t nbytes, void* user);
class PhotoBooth;
class Webcam {
public:
Webcam(PhotoBooth& booth);
~Webcam();
bool setup();
bool start();
bool stop();
void update();
void draw();
private:
PhotoBooth& booth;
VideoCapture cap;
/* GL objects for drawing the cam */
GLuint tex_yuv;
GLuint cam_vao;
GLuint cam_vbo;
GLuint cam_tex;
public:
unsigned char* yuv_pixels;
uv_mutex_t yuv_mutex;
bool has_new_frame;
};
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment