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