nuclear@4: /* nuclear@4: Stereoplay - an OpenGL stereoscopic video player. nuclear@4: Copyright (C) 2011 John Tsiombikas nuclear@4: nuclear@4: This program is free software: you can redistribute it and/or modify nuclear@4: it under the terms of the GNU General Public License as published by nuclear@4: the Free Software Foundation, either version 3 of the License, or nuclear@4: (at your option) any later version. nuclear@4: nuclear@4: This program is distributed in the hope that it will be useful, nuclear@4: but WITHOUT ANY WARRANTY; without even the implied warranty of nuclear@4: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nuclear@4: GNU General Public License for more details. nuclear@4: nuclear@4: You should have received a copy of the GNU General Public License nuclear@4: along with this program. If not, see . nuclear@4: */ 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@1: void sig_decode(int s); nuclear@0: void *shmalloc(size_t sz); nuclear@2: int parse_args(int argc, char **argv); 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@1: int decode_pid; 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@2: const char *vid_fname; nuclear@2: int busy_loop; nuclear@2: nuclear@2: nuclear@0: int main(int argc, char **argv) nuclear@0: { nuclear@2: struct timeval *selwait = 0; nuclear@2: /*char *stereo_combiner, *sdrfile;*/ nuclear@2: nuclear@2: if(parse_args(argc, argv) == -1) { nuclear@2: return 1; nuclear@2: } nuclear@0: nuclear@0: atexit(cleanup); nuclear@0: nuclear@2: if(!(vf = vid_open(vid_fname))) { 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@2: glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_STEREO); 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@2: if(busy_loop) { nuclear@2: selwait = alloca(sizeof *selwait); nuclear@2: selwait->tv_sec = selwait->tv_usec = 0; nuclear@2: } nuclear@2: 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@2: /*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@2: set_uniform_float(sdr, "right_offs", 0.0);*/ nuclear@0: nuclear@0: signal(SIGCHLD, sig); nuclear@0: nuclear@1: if((decode_pid = fork()) == -1) { nuclear@0: perror("failed to fork video decoding process"); nuclear@0: return 1; nuclear@1: } else if(decode_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@6: fcntl(pfd[0], F_SETFL, fcntl(pfd[0], F_GETFL) | O_NONBLOCK); nuclear@6: nuclear@0: dpy = glXGetCurrentDisplay(); nuclear@0: xsock = ConnectionNumber(dpy); nuclear@0: nuclear@0: for(;;) { nuclear@6: /*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@2: res = select((xsock > pfd[0] ? xsock : pfd[0]) + 1, &rdset, 0, 0, selwait); 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@6: }*/ nuclear@0: nuclear@6: upd_frame = 1; nuclear@0: glutMainLoopEvent(); nuclear@2: if(busy_loop) { nuclear@2: glutPostRedisplay(); nuclear@2: } 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@2: nuclear@1: static int paused; nuclear@1: 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@1: signal(SIGUSR1, sig_decode); nuclear@1: 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@1: while(paused) { nuclear@1: pause(); nuclear@1: } nuclear@1: 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@2: glDrawBuffer(swap_eyes ? GL_BACK_RIGHT : GL_BACK_LEFT); nuclear@2: nuclear@0: glBegin(GL_QUADS); nuclear@0: glColor3f(1, 1, 1); nuclear@0: glTexCoord2f(0, 1); glVertex2f(-1, -1); nuclear@2: glTexCoord2f(0.5, 1); glVertex2f(1, -1); nuclear@2: glTexCoord2f(0.5, 0); glVertex2f(1, 1); nuclear@2: glTexCoord2f(0, 0); glVertex2f(-1, 1); nuclear@2: glEnd(); nuclear@2: nuclear@2: glDrawBuffer(swap_eyes ? GL_BACK_LEFT : GL_BACK_RIGHT); nuclear@2: nuclear@2: glBegin(GL_QUADS); nuclear@2: glColor3f(1, 1, 1); nuclear@2: glTexCoord2f(0.5, 1); glVertex2f(-1, -1); nuclear@0: glTexCoord2f(1, 1); glVertex2f(1, -1); nuclear@0: glTexCoord2f(1, 0); glVertex2f(1, 1); nuclear@2: glTexCoord2f(0.5, 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@1: kill(decode_pid, SIGUSR1); nuclear@0: break; nuclear@0: nuclear@0: case 's': nuclear@0: swap_eyes = !swap_eyes; nuclear@2: /*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@2: }*/ 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@1: void sig_decode(int s) nuclear@1: { nuclear@1: signal(s, sig_decode); nuclear@1: nuclear@1: if(s == SIGUSR1) { nuclear@1: paused = !paused; nuclear@1: } nuclear@1: } nuclear@1: 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: } nuclear@2: nuclear@2: int parse_args(int argc, char **argv) nuclear@2: { nuclear@2: int i; nuclear@2: char *method = 0; nuclear@2: nuclear@2: for(i=1; i stereo presentation method\n"); nuclear@2: printf(" -h print usage and exit\n"); nuclear@2: return 0; nuclear@2: nuclear@2: default: nuclear@2: fprintf(stderr, "invalid option: %s\n", argv[i]); nuclear@2: return -1; nuclear@2: } nuclear@2: } else { nuclear@2: if(vid_fname) { nuclear@2: fprintf(stderr, "unexpected argument: %s\n", argv[i]); nuclear@2: return -1; nuclear@2: } nuclear@2: vid_fname = argv[i]; nuclear@2: } nuclear@2: } nuclear@2: nuclear@2: if(!vid_fname) { nuclear@2: fprintf(stderr, "you must specify a video file to open\n"); nuclear@2: return -1; nuclear@2: } nuclear@2: nuclear@2: return 0; nuclear@2: }