nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include "vid.h" nuclear@0: #include "sdr.h" nuclear@0: #include "config.h" nuclear@0: nuclear@0: void cleanup(void); nuclear@0: void decoding_loop(void); nuclear@0: void disp(void); nuclear@0: void reshape(int x, int y); nuclear@0: void keyb(unsigned char key, int x, int y); nuclear@0: void skeyb(int key, int x, int y); nuclear@0: void sig(int s); nuclear@0: void *shmalloc(size_t sz); nuclear@0: nuclear@0: struct video_file *vf; nuclear@0: uint32_t *img; nuclear@0: int vid_xsz, vid_ysz, win_xsz, win_ysz; nuclear@0: unsigned int tex, sdr; nuclear@0: int pfd[2], xsock = -1; nuclear@0: int upd_frame; nuclear@0: int fullscr; nuclear@0: int swap_eyes; nuclear@0: nuclear@0: Display *dpy; nuclear@0: nuclear@0: int main(int argc, char **argv) nuclear@0: { nuclear@0: int pid; nuclear@0: char *stereo_combiner, *sdrfile; nuclear@0: nuclear@0: atexit(cleanup); nuclear@0: nuclear@0: if(!(vf = vid_open(argv[1]))) { nuclear@0: return 1; nuclear@0: } nuclear@0: vid_xsz = win_xsz = vid_frame_width(vf); nuclear@0: vid_ysz = win_ysz = vid_frame_height(vf); nuclear@0: nuclear@0: if(!(img = shmalloc(vid_frame_size(vf)))) { nuclear@0: perror("failed to allocate frame buffer"); nuclear@0: return 1; nuclear@0: } nuclear@0: nuclear@0: if(pipe(pfd) == -1) { nuclear@0: perror("failed to create a self-pipe"); nuclear@0: return 1; nuclear@0: } nuclear@0: nuclear@0: glutInit(&argc, argv); nuclear@0: glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); nuclear@0: glutInitWindowSize(win_xsz, win_ysz); nuclear@0: glutCreateWindow(argv[1]); nuclear@0: nuclear@0: glutDisplayFunc(disp); nuclear@0: glutReshapeFunc(reshape); nuclear@0: glutKeyboardFunc(keyb); nuclear@0: glutSpecialFunc(skeyb); nuclear@0: nuclear@0: glewInit(); nuclear@0: nuclear@0: /* create the frame texture */ nuclear@0: glGenTextures(1, &tex); nuclear@0: glBindTexture(GL_TEXTURE_2D, tex); nuclear@0: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); nuclear@0: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); nuclear@0: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); nuclear@0: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); nuclear@0: glTexImage2D(GL_TEXTURE_2D, 0, 4, vid_xsz, vid_ysz, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); nuclear@0: glEnable(GL_TEXTURE_2D); nuclear@0: nuclear@0: if(!(stereo_combiner = getenv("STEREO_METHOD"))) { nuclear@0: stereo_combiner = "redcyan"; nuclear@0: } nuclear@0: nuclear@0: sdrfile = alloca(strlen(stereo_combiner) + strlen(SDRDIR) + 7); nuclear@0: sprintf(sdrfile, SDRDIR "/%s.glsl", stereo_combiner); nuclear@0: nuclear@0: if(!(sdr = create_program_load(0, sdrfile))) { nuclear@0: return 1; nuclear@0: } nuclear@0: bind_program(sdr); nuclear@0: set_uniform_float(sdr, "left_offs", 0.5); nuclear@0: set_uniform_float(sdr, "right_offs", 0.0); nuclear@0: nuclear@0: signal(SIGCHLD, sig); nuclear@0: nuclear@0: if((pid = fork()) == -1) { nuclear@0: perror("failed to fork video decoding process"); nuclear@0: return 1; nuclear@0: } else if(pid) { nuclear@0: close(pfd[1]); nuclear@0: } else { nuclear@0: close(pfd[0]); nuclear@0: decoding_loop(); nuclear@0: _exit(0); nuclear@0: } nuclear@0: nuclear@0: dpy = glXGetCurrentDisplay(); nuclear@0: xsock = ConnectionNumber(dpy); nuclear@0: nuclear@0: for(;;) { nuclear@0: int res; nuclear@0: fd_set rdset; nuclear@0: nuclear@0: FD_ZERO(&rdset); nuclear@0: FD_SET(xsock, &rdset); nuclear@0: FD_SET(pfd[0], &rdset); nuclear@0: nuclear@0: do { nuclear@0: res = select((xsock > pfd[0] ? xsock : pfd[0]) + 1, &rdset, 0, 0, 0); nuclear@0: } while(res == -1 && errno == EINTR); nuclear@0: nuclear@0: if(FD_ISSET(pfd[0], &rdset)) { nuclear@0: unsigned char done; nuclear@0: read(pfd[0], &done, 1); nuclear@0: nuclear@0: if(done) { nuclear@0: exit(0); nuclear@0: } else { nuclear@0: upd_frame = 1; nuclear@0: glutPostRedisplay(); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: glutMainLoopEvent(); nuclear@0: } nuclear@0: nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: void cleanup(void) nuclear@0: { nuclear@0: if(tex) { nuclear@0: glDeleteTextures(1, &tex); nuclear@0: } nuclear@0: if(img) { nuclear@0: munmap(img, vid_frame_size(vf)); nuclear@0: } nuclear@0: if(vf) { nuclear@0: vid_close(vf); nuclear@0: } nuclear@0: close(pfd[0]); nuclear@0: } nuclear@0: nuclear@0: /* decoding_loop() runs in a separate decoding process and communicates nuclear@0: * with the parent process through the pfd[1] pipe. nuclear@0: */ nuclear@0: void decoding_loop(void) nuclear@0: { nuclear@0: unsigned char done = 0; nuclear@0: struct timespec ts; nuclear@0: struct timeval tv0, tv; nuclear@0: unsigned long frame_nsec = vid_frame_interval(vf) * 1000; nuclear@0: printf("nanosecs per frame: %lu\n", frame_nsec); nuclear@0: nuclear@0: gettimeofday(&tv0, 0); nuclear@0: nuclear@0: while(vid_get_frame(vf, img) != -1) { nuclear@0: write(pfd[1], &done, 1); nuclear@0: nuclear@0: gettimeofday(&tv, 0); nuclear@0: nuclear@0: ts.tv_sec = 0; nuclear@0: ts.tv_nsec = frame_nsec - (tv.tv_usec - tv0.tv_usec) * 1000; nuclear@0: nanosleep(&ts, 0); nuclear@0: nuclear@0: gettimeofday(&tv0, 0); nuclear@0: } nuclear@0: nuclear@0: done = 1; nuclear@0: write(pfd[1], &done, 1); nuclear@0: } nuclear@0: nuclear@0: void disp(void) nuclear@0: { nuclear@0: if(upd_frame) { nuclear@0: /* frame changed, we must re-upload the texture */ nuclear@0: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, vid_xsz, vid_ysz, GL_BGRA, GL_UNSIGNED_BYTE, img); nuclear@0: upd_frame = 0; nuclear@0: } nuclear@0: nuclear@0: glBegin(GL_QUADS); nuclear@0: glColor3f(1, 1, 1); nuclear@0: glTexCoord2f(0, 1); glVertex2f(-1, -1); nuclear@0: glTexCoord2f(1, 1); glVertex2f(1, -1); nuclear@0: glTexCoord2f(1, 0); glVertex2f(1, 1); nuclear@0: glTexCoord2f(0, 0); glVertex2f(-1, 1); nuclear@0: glEnd(); nuclear@0: nuclear@0: glutSwapBuffers(); nuclear@0: } nuclear@0: nuclear@0: void reshape(int x, int y) nuclear@0: { nuclear@0: if(!fullscr) { nuclear@0: win_xsz = x; nuclear@0: win_ysz = y; nuclear@0: } nuclear@0: nuclear@0: glViewport(0, 0, x, y); nuclear@0: } nuclear@0: nuclear@0: void keyb(unsigned char key, int x, int y) nuclear@0: { nuclear@0: switch(key) { nuclear@0: case 'q': nuclear@0: exit(0); nuclear@0: nuclear@0: case 'f': nuclear@0: fullscr = !fullscr; nuclear@0: if(fullscr) { nuclear@0: glutFullScreen(); nuclear@0: } else { nuclear@0: glutReshapeWindow(win_xsz, win_ysz); nuclear@0: } nuclear@0: break; nuclear@0: nuclear@0: case ' ': nuclear@0: /* TODO pause/resume */ nuclear@0: break; nuclear@0: nuclear@0: case 's': nuclear@0: swap_eyes = !swap_eyes; nuclear@0: if(swap_eyes) { nuclear@0: set_uniform_float(sdr, "left_offs", 0.0); nuclear@0: set_uniform_float(sdr, "right_offs", 0.5); nuclear@0: } else { nuclear@0: set_uniform_float(sdr, "left_offs", 0.5); nuclear@0: set_uniform_float(sdr, "right_offs", 0.0); nuclear@0: } nuclear@0: break; nuclear@0: nuclear@0: default: nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: void skeyb(int key, int x, int y) nuclear@0: { nuclear@0: switch(key) { nuclear@0: case GLUT_KEY_LEFT: nuclear@0: /* TODO skip fwd */ nuclear@0: break; nuclear@0: nuclear@0: case GLUT_KEY_RIGHT: nuclear@0: /* TODO skip back */ nuclear@0: break; nuclear@0: nuclear@0: case GLUT_KEY_UP: nuclear@0: /* TODO skip fwd more */ nuclear@0: break; nuclear@0: nuclear@0: case GLUT_KEY_DOWN: nuclear@0: /* TODO skip back more */ nuclear@0: break; nuclear@0: nuclear@0: case GLUT_KEY_PAGE_UP: nuclear@0: /* TODO skip fwd a lot */ nuclear@0: break; nuclear@0: nuclear@0: case GLUT_KEY_PAGE_DOWN: nuclear@0: /* TODO skip back a lot */ nuclear@0: break; nuclear@0: nuclear@0: default: nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: void sig(int s) nuclear@0: { nuclear@0: if(s == SIGCHLD) { nuclear@0: wait(0); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: void *shmalloc(size_t sz) nuclear@0: { nuclear@0: int fd; nuclear@0: void *shm; nuclear@0: nuclear@0: if(!(fd = open("/dev/zero", O_RDWR))) { nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: if((shm = mmap(0, sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == (void*)-1) { nuclear@0: shm = 0; nuclear@0: } nuclear@0: nuclear@0: close(fd); nuclear@0: return shm; nuclear@0: }