Skip to content

Instantly share code, notes, and snippets.

@roxlu
Last active December 24, 2015 04:49
Show Gist options
  • Save roxlu/6746543 to your computer and use it in GitHub Desktop.
Save roxlu/6746543 to your computer and use it in GitHub Desktop.
YUV shader (I420p)
extern "C" {
# include <uv.h>
}
#include <iostream>
#include <streamer/videostreamer/VideoStreamer.h>
#include <decklink/DeckLink.h>
#include <libyuv/libyuv.h>
#include <tinylib/tinylib.h>
#include <GLXW/glxw.h>
#include <GLFW/glfw3.h>
#include <decklink_streamer/FastI420Upload.h>
#define WRITE_YUV_TO_FILE 0
#if WRITE_YUV_TO_FILE
# include <fstream>
#endif
static const char* DEBUG_VS = ""
"#version 150\n"
"const vec2 verts[4] = vec2[] ("
" vec2(-1.0, 1.0), "
" vec2(-1.0, -1.0), "
" vec2(1.0, 1.0), "
" vec2(1.0, -1.0) "
");"
"const vec2 texcoords[4] = vec2[] ("
" vec2(0.0, 0.0), "
" vec2(0.0, 1.0), "
" vec2(1.0, 0.0), "
" vec2(1.0, 1.0) "
");"
"out vec2 v_texcoord;"
"void main() {"
" gl_Position = vec4(verts[gl_VertexID], 0.0, 1.0);"
" v_texcoord = texcoords[gl_VertexID];"
"}"
;
static const char* DEBUG_FS = ""
"#version 150\n"
"uniform sampler2D y_tex;"
"uniform sampler2D u_tex;"
"uniform sampler2D v_tex;"
"in vec2 v_texcoord;"
"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() {"
" float y = texture(y_tex, v_texcoord).r;"
" float u = texture(u_tex, v_texcoord).r;"
" float v = texture(v_tex, v_texcoord).r;"
" fragcolor.a = 1.0; "
#if 1
" vec3 yuv = vec3(y,u,v);"
" yuv += offset;"
" fragcolor = vec4(0.0, 0.0, 0.0, 1.0);"
" fragcolor.r = dot(yuv, R_cf);"
" fragcolor.g = dot(yuv, G_cf);"
" fragcolor.b = dot(yuv, B_cf);"
#else
// source: http://www.fourcc.org/source/YUV420P-OpenGL-GLSLang.c
" y = 1.1643 * (y-0.0625);"
" u = u - 0.5; "
" v = v - 0.5;"
" fragcolor.r = y + 1.5958 * v;"
" fragcolor.g = y - 0.39173 * u - 0.81290 * v;"
" fragcolor.b = y + 2.017 * u;"
#endif
"}";
void key_callback(GLFWwindow* win, int key, int scancode, int action, int mods);
void error_callback(int err, const char* desc);
void resize_callback(GLFWwindow* window, int width, int height);
void frame_callback(IDeckLinkVideoInputFrame* vframe, IDeckLinkAudioInputPacket* aframe, void* user);
FastI420Upload fast_upload;
uint8_t yuv420p[1382400]; // our buffer for yuv420p 1280x720
uv_mutex_t frame_mutex;
bool new_frame;
GLuint vao;
GLuint vert;
GLuint frag;
GLuint prog;
#if WRITE_YUV_TO_FILE
std::ofstream ofs;
#endif
int main() {
printf("DeckLink Stream.\n");
if(!glfwInit()) {
printf("error: cannot setup glfw.\n");
return false;
}
new_frame = false;
DeckLink dl;
dl.printDevices();
if(!dl.setup(0)) {
::exit(EXIT_FAILURE);
}
if(!dl.setCallback(frame_callback, NULL)) {
::exit(EXIT_FAILURE);
}
if(!dl.setVideoMode(bmdModeHD720p60, bmdFormat8BitYUV)) {
::exit(EXIT_FAILURE);
}
glfwWindowHint(GLFW_SAMPLES, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* win = NULL;
int w = 1280;
int h = 720;
win = glfwCreateWindow(w, h, "DeckLink Streamer", NULL, NULL);
if(!win) {
glfwTerminate();
exit(EXIT_FAILURE);
}
glfwSetFramebufferSizeCallback(win, resize_callback);
glfwSetKeyCallback(win, key_callback);
glfwMakeContextCurrent(win);
glfwSwapInterval(1);
// before calling any other GL function you need to initialize!
if(glxwInit() != 0) {
printf("error: cannot initialize glxw.\n");
::exit(EXIT_FAILURE);
}
glDisable(GL_DEPTH_TEST);
glEnable(GL_MULTISAMPLE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
#if WRITE_YUV_TO_FILE
ofs.open("yuv.raw", std::ios::out | std::ios::binary);
if(!ofs.is_open()) {
printf("error: cannot output output file.\n");
::exit(EXIT_FAILURE);
}
#endif
uv_mutex_init(&frame_mutex);
if(!fast_upload.setup(1280, 720)) {
::exit(EXIT_FAILURE);
}
if(!dl.start()) {
::exit(EXIT_FAILURE);
}
w = 1280;
h = 720;
uint8_t* dest_y = yuv420p;
uint8_t* dest_u = &yuv420p[(w * h)];
uint8_t* dest_v = &yuv420p[(uint32_t)((w * h) + (w >> 1 ) * (h >> 1))];
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
vert = rx_create_shader(GL_VERTEX_SHADER, DEBUG_VS);
frag = rx_create_shader(GL_FRAGMENT_SHADER, DEBUG_FS);
prog = rx_create_program(vert, frag);
glLinkProgram(prog);
glUseProgram(prog);
glUniform1i(glGetUniformLocation(prog, "y_tex"), 0);
glUniform1i(glGetUniformLocation(prog, "u_tex"), 1);
glUniform1i(glGetUniformLocation(prog, "v_tex"), 2);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, fast_upload.tex_y);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, fast_upload.tex_u);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, fast_upload.tex_y);
while(!glfwWindowShouldClose(win)) {
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
uv_mutex_lock(&frame_mutex);
{
if(new_frame) {
fast_upload.update(dest_y, dest_u, dest_v);
new_frame = false;
}
}
uv_mutex_unlock(&frame_mutex);
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glfwSwapBuffers(win);
glfwPollEvents();
}
dl.stop();
uv_mutex_destroy(&frame_mutex);
#if WRITE_YUV_TO_FILE
if(ofs.is_open()) {
ofs.close();
}
#endif
glfwTerminate();
return EXIT_SUCCESS;
}
void error_callback(int err, const char* desc) {
printf("glfw error: %s (%d)\n", desc, err);
}
void key_callback(GLFWwindow* win, int key, int scancode, int action, int mods) {
if(action != GLFW_PRESS) {
return;
}
switch(key) {
case GLFW_KEY_LEFT: {
break;
}
case GLFW_KEY_RIGHT: {
break;
}
case GLFW_KEY_ESCAPE: {
glfwSetWindowShouldClose(win, GL_TRUE);
break;
}
};
}
void resize_callback(GLFWwindow* window, int width, int height) {
}
void frame_callback(IDeckLinkVideoInputFrame* vframe, IDeckLinkAudioInputPacket* aframe, void* user) {
if(!vframe) {
printf("-- invalid vframe.\n");
return;
}
uint32_t w = vframe->GetWidth();
uint32_t h = vframe->GetHeight();
uint32_t stride = vframe->GetRowBytes();
uint8_t* uyvy422 = NULL;
HRESULT r = vframe->GetBytes((void**)&uyvy422);
if(r != S_OK) {
printf("error: cannot get yuv bytes.\n");
return ;
}
uv_mutex_lock(&frame_mutex);
{
new_frame = true;
uint8_t* dest_y = yuv420p;
uint8_t* dest_u = &yuv420p[(w * h)];
uint8_t* dest_v = &yuv420p[(uint32_t)((w * h) + (w >> 1 ) * (h >> 1))];
uint64_t n = uv_hrtime();
libyuv::UYVYToI420(uyvy422, stride,
dest_y, w,
dest_u, w >> 1,
dest_v, w >> 1,
w, h);
uint64_t d = uv_hrtime() - n;
#if WRITE_YUV_TO_FILE
if(ofs.is_open()) {
size_t nbytes = (w * h) + (2 * (w >> 1) * (h >> 1));
printf("writing..%ld\n", nbytes);
ofs.write((char*)yuv420p, nbytes);
}
#endif
}
uv_mutex_unlock(&frame_mutex);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment