stereoplay

annotate src/stereoplay.c @ 6:8fc045d33d62

updated the code to work with more recent ffmpeg versions
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 26 Sep 2020 14:26:39 +0300
parents acf3d25f23cb
children
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 }