stereoplay

view src/stereoplay.c @ 4:acf3d25f23cb

added license headings
author John Tsiombikas <nuclear@member.fsf.org>
date Thu, 03 Nov 2011 21:57:12 +0200
parents fd671d488cfd
children 8fc045d33d62
line source
1 /*
2 Stereoplay - an OpenGL stereoscopic video player.
3 Copyright (C) 2011 John Tsiombikas <nuclear@member.fsf.org>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <signal.h>
23 #include <time.h>
24 #include <assert.h>
25 #include <alloca.h>
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <sys/mman.h>
29 #include <sys/wait.h>
30 #include <sys/time.h>
31 #include <sys/select.h>
32 #include <X11/Xlib.h>
33 #include <GL/glew.h>
34 #include <GL/glut.h>
35 #include <GL/freeglut_ext.h>
36 #include <GL/glx.h>
37 #include "vid.h"
38 #include "sdr.h"
39 #include "config.h"
41 void cleanup(void);
42 void decoding_loop(void);
43 void disp(void);
44 void reshape(int x, int y);
45 void keyb(unsigned char key, int x, int y);
46 void skeyb(int key, int x, int y);
47 void sig(int s);
48 void sig_decode(int s);
49 void *shmalloc(size_t sz);
50 int parse_args(int argc, char **argv);
52 struct video_file *vf;
53 uint32_t *img;
54 int vid_xsz, vid_ysz, win_xsz, win_ysz;
55 unsigned int tex, sdr;
56 int decode_pid;
57 int pfd[2], xsock = -1;
58 int upd_frame;
59 int fullscr;
60 int swap_eyes;
62 Display *dpy;
64 const char *vid_fname;
65 int busy_loop;
68 int main(int argc, char **argv)
69 {
70 struct timeval *selwait = 0;
71 /*char *stereo_combiner, *sdrfile;*/
73 if(parse_args(argc, argv) == -1) {
74 return 1;
75 }
77 atexit(cleanup);
79 if(!(vf = vid_open(vid_fname))) {
80 return 1;
81 }
82 vid_xsz = win_xsz = vid_frame_width(vf);
83 vid_ysz = win_ysz = vid_frame_height(vf);
85 if(!(img = shmalloc(vid_frame_size(vf)))) {
86 perror("failed to allocate frame buffer");
87 return 1;
88 }
90 if(pipe(pfd) == -1) {
91 perror("failed to create a self-pipe");
92 return 1;
93 }
95 glutInit(&argc, argv);
96 glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_STEREO);
97 glutInitWindowSize(win_xsz, win_ysz);
98 glutCreateWindow(argv[1]);
100 glutDisplayFunc(disp);
101 glutReshapeFunc(reshape);
102 glutKeyboardFunc(keyb);
103 glutSpecialFunc(skeyb);
105 if(busy_loop) {
106 selwait = alloca(sizeof *selwait);
107 selwait->tv_sec = selwait->tv_usec = 0;
108 }
110 glewInit();
112 /* create the frame texture */
113 glGenTextures(1, &tex);
114 glBindTexture(GL_TEXTURE_2D, tex);
115 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
116 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
117 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
118 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
119 glTexImage2D(GL_TEXTURE_2D, 0, 4, vid_xsz, vid_ysz, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
120 glEnable(GL_TEXTURE_2D);
122 /*if(!(stereo_combiner = getenv("STEREO_METHOD"))) {
123 stereo_combiner = "redcyan";
124 }
126 sdrfile = alloca(strlen(stereo_combiner) + strlen(SDRDIR) + 7);
127 sprintf(sdrfile, SDRDIR "/%s.glsl", stereo_combiner);
129 if(!(sdr = create_program_load(0, sdrfile))) {
130 return 1;
131 }
132 bind_program(sdr);
133 set_uniform_float(sdr, "left_offs", 0.5);
134 set_uniform_float(sdr, "right_offs", 0.0);*/
136 signal(SIGCHLD, sig);
138 if((decode_pid = fork()) == -1) {
139 perror("failed to fork video decoding process");
140 return 1;
141 } else if(decode_pid) {
142 close(pfd[1]);
143 } else {
144 close(pfd[0]);
145 decoding_loop();
146 _exit(0);
147 }
149 dpy = glXGetCurrentDisplay();
150 xsock = ConnectionNumber(dpy);
152 for(;;) {
153 int res;
154 fd_set rdset;
156 FD_ZERO(&rdset);
157 FD_SET(xsock, &rdset);
158 FD_SET(pfd[0], &rdset);
160 do {
161 res = select((xsock > pfd[0] ? xsock : pfd[0]) + 1, &rdset, 0, 0, selwait);
162 } while(res == -1 && errno == EINTR);
164 if(FD_ISSET(pfd[0], &rdset)) {
165 unsigned char done;
166 read(pfd[0], &done, 1);
168 if(done) {
169 exit(0);
170 } else {
171 upd_frame = 1;
172 glutPostRedisplay();
173 }
174 }
176 glutMainLoopEvent();
177 if(busy_loop) {
178 glutPostRedisplay();
179 }
180 }
182 return 0;
183 }
185 void cleanup(void)
186 {
187 if(tex) {
188 glDeleteTextures(1, &tex);
189 }
190 if(img) {
191 munmap(img, vid_frame_size(vf));
192 }
193 if(vf) {
194 vid_close(vf);
195 }
196 close(pfd[0]);
197 }
200 static int paused;
202 /* decoding_loop() runs in a separate decoding process and communicates
203 * with the parent process through the pfd[1] pipe.
204 */
205 void decoding_loop(void)
206 {
207 unsigned char done = 0;
208 struct timespec ts;
209 struct timeval tv0, tv;
210 unsigned long frame_nsec = vid_frame_interval(vf) * 1000;
211 printf("nanosecs per frame: %lu\n", frame_nsec);
213 signal(SIGUSR1, sig_decode);
215 gettimeofday(&tv0, 0);
217 while(vid_get_frame(vf, img) != -1) {
218 write(pfd[1], &done, 1);
220 gettimeofday(&tv, 0);
222 ts.tv_sec = 0;
223 ts.tv_nsec = frame_nsec - (tv.tv_usec - tv0.tv_usec) * 1000;
224 nanosleep(&ts, 0);
226 while(paused) {
227 pause();
228 }
230 gettimeofday(&tv0, 0);
231 }
233 done = 1;
234 write(pfd[1], &done, 1);
235 }
237 void disp(void)
238 {
239 if(upd_frame) {
240 /* frame changed, we must re-upload the texture */
241 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, vid_xsz, vid_ysz, GL_BGRA, GL_UNSIGNED_BYTE, img);
242 upd_frame = 0;
243 }
245 glDrawBuffer(swap_eyes ? GL_BACK_RIGHT : GL_BACK_LEFT);
247 glBegin(GL_QUADS);
248 glColor3f(1, 1, 1);
249 glTexCoord2f(0, 1); glVertex2f(-1, -1);
250 glTexCoord2f(0.5, 1); glVertex2f(1, -1);
251 glTexCoord2f(0.5, 0); glVertex2f(1, 1);
252 glTexCoord2f(0, 0); glVertex2f(-1, 1);
253 glEnd();
255 glDrawBuffer(swap_eyes ? GL_BACK_LEFT : GL_BACK_RIGHT);
257 glBegin(GL_QUADS);
258 glColor3f(1, 1, 1);
259 glTexCoord2f(0.5, 1); glVertex2f(-1, -1);
260 glTexCoord2f(1, 1); glVertex2f(1, -1);
261 glTexCoord2f(1, 0); glVertex2f(1, 1);
262 glTexCoord2f(0.5, 0); glVertex2f(-1, 1);
263 glEnd();
265 glutSwapBuffers();
266 }
268 void reshape(int x, int y)
269 {
270 if(!fullscr) {
271 win_xsz = x;
272 win_ysz = y;
273 }
275 glViewport(0, 0, x, y);
276 }
278 void keyb(unsigned char key, int x, int y)
279 {
280 switch(key) {
281 case 'q':
282 exit(0);
284 case 'f':
285 fullscr = !fullscr;
286 if(fullscr) {
287 glutFullScreen();
288 } else {
289 glutReshapeWindow(win_xsz, win_ysz);
290 }
291 break;
293 case ' ':
294 kill(decode_pid, SIGUSR1);
295 break;
297 case 's':
298 swap_eyes = !swap_eyes;
299 /*if(swap_eyes) {
300 set_uniform_float(sdr, "left_offs", 0.0);
301 set_uniform_float(sdr, "right_offs", 0.5);
302 } else {
303 set_uniform_float(sdr, "left_offs", 0.5);
304 set_uniform_float(sdr, "right_offs", 0.0);
305 }*/
306 break;
308 default:
309 break;
310 }
311 }
313 void skeyb(int key, int x, int y)
314 {
315 switch(key) {
316 case GLUT_KEY_LEFT:
317 /* TODO skip fwd */
318 break;
320 case GLUT_KEY_RIGHT:
321 /* TODO skip back */
322 break;
324 case GLUT_KEY_UP:
325 /* TODO skip fwd more */
326 break;
328 case GLUT_KEY_DOWN:
329 /* TODO skip back more */
330 break;
332 case GLUT_KEY_PAGE_UP:
333 /* TODO skip fwd a lot */
334 break;
336 case GLUT_KEY_PAGE_DOWN:
337 /* TODO skip back a lot */
338 break;
340 default:
341 break;
342 }
343 }
345 void sig(int s)
346 {
347 if(s == SIGCHLD) {
348 wait(0);
349 }
350 }
352 void sig_decode(int s)
353 {
354 signal(s, sig_decode);
356 if(s == SIGUSR1) {
357 paused = !paused;
358 }
359 }
361 void *shmalloc(size_t sz)
362 {
363 int fd;
364 void *shm;
366 if(!(fd = open("/dev/zero", O_RDWR))) {
367 return 0;
368 }
370 if((shm = mmap(0, sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == (void*)-1) {
371 shm = 0;
372 }
374 close(fd);
375 return shm;
376 }
378 int parse_args(int argc, char **argv)
379 {
380 int i;
381 char *method = 0;
383 for(i=1; i<argc; i++) {
384 if(argv[i][0] == '-' && argv[i][2] == 0) {
385 switch(argv[i][1]) {
386 case 'b':
387 busy_loop = 1;
388 printf("busy looping!\n");
389 break;
391 case 's':
392 method = argv[++i];
393 break;
395 case 'h':
396 printf("Usage: %s [options]\n", argv[0]);
397 printf("options:\n");
398 printf(" -b busy loop (redraw continuously)\n");
399 printf(" -s <method> stereo presentation method\n");
400 printf(" -h print usage and exit\n");
401 return 0;
403 default:
404 fprintf(stderr, "invalid option: %s\n", argv[i]);
405 return -1;
406 }
407 } else {
408 if(vid_fname) {
409 fprintf(stderr, "unexpected argument: %s\n", argv[i]);
410 return -1;
411 }
412 vid_fname = argv[i];
413 }
414 }
416 if(!vid_fname) {
417 fprintf(stderr, "you must specify a video file to open\n");
418 return -1;
419 }
421 return 0;
422 }