1 /*
2 plaympeg - Sample MPEG player using the SMPEG library
3 Copyright (C) 1999 Loki Entertainment Software
4
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 2 of the License, or
8 (at your option) any later version.
9
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.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <signal.h>
24
25 #ifdef unix
26 #include <unistd.h>
27
28 #include <errno.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <sys/ioctl.h>
32 #include <sys/time.h>
33
34 #define NET_SUPPORT /* General network support */
35 #define RAW_SUPPORT /* Raw data transport support */
36 #define HTTP_SUPPORT /* HTTP support */
37 #define FTP_SUPPORT /* FTP support */
38 #ifdef linux
39 #define VCD_SUPPORT /* Video CD support */
40 #endif
41 #endif
42
43 #ifdef NET_SUPPORT
44 #include <netinet/in.h>
45 #include <netdb.h>
46 #include <sys/socket.h>
47 #include <arpa/inet.h>
48 #endif
49
50 #ifdef VCD_SUPPORT
51 #include <signal.h>
52 #include <fcntl.h>
53 #include <sys/stat.h>
54 #include <linux/cdrom.h>
55 #endif
56
57 #include "smpeg.h"
58
59
usage(char * argv0)60 void usage(char *argv0)
61 {
62 printf(
63 "Usage: %s [options] file ...\n"
64 "Where the options are one of:\n"
65 " --noaudio Don't play audio stream\n"
66 " --novideo Don't play video stream\n"
67 " --fullscreen Play MPEG in fullscreen mode\n"
68 " --double or -2 Play MPEG at double size\n"
69 " --loop or -l Play MPEG over and over\n"
70 " --bilinear Use bilinear filtering\n"
71 " --volume N or -v N Set audio volume to N (0-100)\n"
72 " --title T or -t T Set window's title to T\n"
73 " --scale wxh or -s wxh Play MPEG at given resolution\n"
74 " --seek N or -S N Skip N bytes\n"
75 #ifdef USE_SYSTEM_TIMESTAMP
76 " --skip N or -k N Skip N seconds\n"
77 #endif
78 " --help or -h\n"
79 " --version or -V\n"
80 "Specifying - as filename will use stdin for input\n", argv0);
81 }
82
83 #ifdef NET_SUPPORT
is_address_multicast(unsigned long address)84 int is_address_multicast(unsigned long address)
85 {
86 if((address & 255) >= 224 && (address & 255) <= 239) return(1);
87 return(0);
88 }
89
tcp_open(char * address,int port)90 int tcp_open(char * address, int port)
91 {
92 struct sockaddr_in stAddr;
93 struct hostent * host;
94 int sock;
95 struct linger l;
96
97 memset(&stAddr,0,sizeof(stAddr));
98 stAddr.sin_family = AF_INET ;
99 stAddr.sin_port = htons(port);
100
101 if((host = gethostbyname(address)) == NULL) return(0);
102
103 stAddr.sin_addr = *((struct in_addr *) host->h_addr_list[0]) ;
104
105 if((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) return(0);
106
107 l.l_onoff = 1; l.l_linger = 5;
108 if(setsockopt(sock, SOL_SOCKET, SO_LINGER, (char*) &l, sizeof(l)) < 0) return(0);
109
110 if(connect(sock, (struct sockaddr *) &stAddr, sizeof(stAddr)) < 0) return(0);
111
112 return(sock);
113 }
114
udp_open(char * address,int port)115 int udp_open(char * address, int port)
116 {
117 int enable = 1L;
118 struct sockaddr_in stAddr;
119 struct sockaddr_in stLclAddr;
120 struct ip_mreq stMreq;
121 struct hostent * host;
122 int sock;
123
124 stAddr.sin_family = AF_INET;
125 stAddr.sin_port = htons(port);
126
127 if((host = gethostbyname(address)) == NULL) return(0);
128
129 stAddr.sin_addr = *((struct in_addr *) host->h_addr_list[0]) ;
130
131 /* Create a UDP socket */
132 if((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) return(0);
133
134 /* Allow multiple instance of the client to share the same address and port */
135 if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &enable, sizeof(unsigned long int)) < 0) return(0);
136
137 /* If the address is multicast, register to the multicast group */
138 if(is_address_multicast(stAddr.sin_addr.s_addr))
139 {
140 /* Bind the socket to port */
141 stLclAddr.sin_family = AF_INET;
142 stLclAddr.sin_addr.s_addr = htonl(INADDR_ANY);
143 stLclAddr.sin_port = stAddr.sin_port;
144 if(bind(sock, (struct sockaddr*) & stLclAddr, sizeof(stLclAddr)) < 0) return(0);
145
146 /* Register to a multicast address */
147 stMreq.imr_multiaddr.s_addr = stAddr.sin_addr.s_addr;
148 stMreq.imr_interface.s_addr = INADDR_ANY;
149 if(setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) & stMreq, sizeof(stMreq)) < 0) return(0);
150 }
151 else
152 {
153 /* Bind the socket to port */
154 stLclAddr.sin_family = AF_INET;
155 stLclAddr.sin_addr.s_addr = htonl(INADDR_ANY);
156 stLclAddr.sin_port = htons(0);
157 if(bind(sock, (struct sockaddr*) & stLclAddr, sizeof(stLclAddr)) < 0) return(0);
158 }
159
160 return(sock);
161 }
162
163 #ifdef RAW_SUPPORT
raw_open(char * arg)164 int raw_open(char * arg)
165 {
166 char * host;
167 int port;
168 int sock;
169
170 /* Check for URL syntax */
171 if(strncmp(arg, "raw://", strlen("raw://"))) return(0);
172
173 /* Parse URL */
174 port = 0;
175 host = arg + strlen("raw://");
176 if(strchr(host, ':') != NULL) /* port is specified */
177 {
178 port = atoi(strchr(host, ':') + 1);
179 *strchr(host, ':') = 0;
180 }
181
182 /* Open a UDP socket */
183 if(!(sock = udp_open(host, port)))
184 perror("raw_open");
185
186 return(sock);
187 }
188 #endif
189
190 #ifdef HTTP_SUPPORT
http_open(char * arg)191 int http_open(char * arg)
192 {
193 char * host;
194 int port;
195 char * request;
196 int tcp_sock;
197 char http_request[1024];
198 char c;
199
200 /* Check for URL syntax */
201 if(strncmp(arg, "http://", strlen("http://"))) return(0);
202
203 /* Parse URL */
204 port = 80;
205 host = arg + strlen("http://");
206 if((request = strchr(host, '/')) == NULL) return(0);
207 *request++ = 0;
208 if(strchr(host, ':') != NULL) /* port is specified */
209 {
210 port = atoi(strchr(host, ':') + 1);
211 *strchr(host, ':') = 0;
212 }
213
214 /* Open a TCP socket */
215 if(!(tcp_sock = tcp_open(host, port)))
216 {
217 perror("http_open");
218 return(0);
219 }
220
221 /* Send HTTP GET request */
222 sprintf(http_request,
223 "GET /%s HTTP/1.0\r\n"
224 "User-Agent: Mozilla/2.0 (Win95; I)\r\n"
225 "Pragma: no-cache\r\n"
226 "Host: %s\r\n"
227 "Accept: */*\r\n"
228 "\r\n",
229 request, host);
230 send(tcp_sock, http_request, strlen(http_request), 0);
231
232 /* Parse server reply */
233 do read(tcp_sock, &c, sizeof(char)); while(c != ' ');
234 read(tcp_sock, http_request, 4*sizeof(char));
235 http_request[4] = 0;
236 if(strcmp(http_request, "200 "))
237 {
238 fprintf(stderr, "http_open: ");
239 do {
240 read(tcp_sock, &c, sizeof(char));
241 fprintf(stderr, "%c", c);
242 }
243 while(c != '\r');
244 fprintf(stderr, "\n");
245 return(0);
246 }
247
248 return(tcp_sock);
249 }
250 #endif
251 #ifdef FTP_SUPPORT
ftp_get_reply(int tcp_sock)252 int ftp_get_reply(int tcp_sock)
253 {
254 int i;
255 char c;
256 char answer[1024];
257
258 do {
259 /* Read a line */
260 for(i = 0, c = 0; i < 1024 && c != '\n'; i++)
261 {
262 read(tcp_sock, &c, sizeof(char));
263 answer[i] = c;
264 }
265 answer[i] = 0;
266 fprintf(stderr, "%s", answer + 4);
267 }
268 while(answer[3] == '-');
269
270 answer[3] = 0;
271
272 return(atoi(answer));
273 }
274
ftp_open(char * arg)275 int ftp_open(char * arg)
276 {
277 char * host;
278 int port;
279 char * dir;
280 char * file;
281 int tcp_sock;
282 int data_sock;
283 char ftp_request[1024];
284 struct sockaddr_in stLclAddr;
285 socklen_t namelen;
286 int i;
287
288 /* Check for URL syntax */
289 if(strncmp(arg, "ftp://", strlen("ftp://"))) return(0);
290
291 /* Parse URL */
292 port = 21;
293 host = arg + strlen("ftp://");
294 if((dir = strchr(host, '/')) == NULL) return(0);
295 *dir++ = 0;
296 if((file = strrchr(dir, '/')) == NULL) {
297 file = dir;
298 dir = NULL;
299 } else
300 *file++ = 0;
301
302 if(strchr(host, ':') != NULL) /* port is specified */
303 {
304 port = atoi(strchr(host, ':') + 1);
305 *strchr(host, ':') = 0;
306 }
307
308 /* Open a TCP socket */
309 if(!(tcp_sock = tcp_open(host, port)))
310 {
311 perror("ftp_open");
312 return(0);
313 }
314
315 /* Send FTP USER and PASS request */
316 ftp_get_reply(tcp_sock);
317 sprintf(ftp_request, "USER anonymous\r\n");
318 send(tcp_sock, ftp_request, strlen(ftp_request), 0);
319 if(ftp_get_reply(tcp_sock) != 331) return(0);
320 sprintf(ftp_request, "PASS smpeguser@\r\n");
321 send(tcp_sock, ftp_request, strlen(ftp_request), 0);
322 if(ftp_get_reply(tcp_sock) != 230) return(0);
323 sprintf(ftp_request, "TYPE I\r\n");
324 send(tcp_sock, ftp_request, strlen(ftp_request), 0);
325 if(ftp_get_reply(tcp_sock) != 200) return(0);
326 if(dir != NULL)
327 {
328 sprintf(ftp_request, "CWD %s\r\n", dir);
329 send(tcp_sock, ftp_request, strlen(ftp_request), 0);
330 if(ftp_get_reply(tcp_sock) != 250) return(0);
331 }
332
333 /* Get interface address */
334 namelen = sizeof(stLclAddr);
335 if(getsockname(tcp_sock, (struct sockaddr *) &stLclAddr, &namelen) < 0)
336 return(0);
337
338 /* Open data socket */
339 if ((data_sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) return(0);
340
341 stLclAddr.sin_family = AF_INET;
342
343 /* Get the first free port */
344 for(i = 0; i < 0xC000; i++) {
345 stLclAddr.sin_port = htons(0x4000 + i);
346 if(bind(data_sock, (struct sockaddr *) &stLclAddr, sizeof(stLclAddr)) >= 0) break;
347 }
348 port = 0x4000 + i;
349
350 if(listen(data_sock, 1) < 0) return(0);
351
352 i = ntohl(stLclAddr.sin_addr.s_addr);
353 sprintf(ftp_request, "PORT %d,%d,%d,%d,%d,%d\r\n",
354 (i >> 24) & 0xFF, (i >> 16) & 0xFF,
355 (i >> 8) & 0xFF, i & 0xFF,
356 (port >> 8) & 0xFF, port & 0xFF);
357 send(tcp_sock, ftp_request, strlen(ftp_request), 0);
358 if(ftp_get_reply(tcp_sock) != 200) return(0);
359
360 sprintf(ftp_request, "RETR %s\r\n", file);
361 send(tcp_sock, ftp_request, strlen(ftp_request), 0);
362 if(ftp_get_reply(tcp_sock) != 150) return(0);
363
364 return(accept(data_sock, NULL, NULL));
365 }
366 #endif
367 #endif
368
369 #ifdef VCD_SUPPORT
vcd_read(int fd,int lba,unsigned char * buf)370 int vcd_read(int fd, int lba, unsigned char *buf)
371 {
372 struct cdrom_msf *msf;
373
374 msf = (struct cdrom_msf*) buf;
375 msf->cdmsf_min0 = (lba + CD_MSF_OFFSET) / CD_FRAMES / CD_SECS;
376 msf->cdmsf_sec0 = (lba + CD_MSF_OFFSET) / CD_FRAMES % CD_SECS;
377 msf->cdmsf_frame0 = (lba + CD_MSF_OFFSET) % CD_FRAMES;
378 return(ioctl(fd, CDROMREADMODE2, buf));
379 }
380
vcd_open(char * arg)381 int vcd_open(char * arg)
382 {
383 struct stat buf;
384 struct cdrom_tocentry toc;
385 char *pip;
386 int track;
387 int pipe_fd[2];
388 int fd;
389 int pid, parent;
390 unsigned char * buffer;
391
392 /* Track defaults to 02, unless requested otherwise */
393 track = 02;
394 pip = strrchr(arg, ':');
395 if ( pip ) {
396 *pip = '\0';
397 track = atoi(pip+1) + 1;
398 }
399
400 /* See if the CD-ROM device file exists */
401 if ( (stat(arg, &buf) < 0) || !S_ISBLK(buf.st_mode) ) {
402 if ( pip ) {
403 *pip = ':';
404 }
405 return(0);
406 }
407
408 fd = open(arg, O_RDONLY, 0);
409 if ( fd < 0 ) {
410 if ( pip ) {
411 *pip = ':';
412 }
413 return(0);
414 }
415
416 /* Track 02 (changed to 'track') contains MPEG data */
417 if ( track < 2 ) {
418 printf("Warning: VCD data normally starts on track 2\n");
419 }
420 toc.cdte_track = track;
421 toc.cdte_format = CDROM_LBA;
422 if(ioctl(fd, CDROMREADTOCENTRY, &toc) < 0) return(0);
423
424 if(pipe(pipe_fd) < 0) return(0);
425
426 parent = getpid();
427 pid = fork();
428
429 if(pid < 0) return(0);
430
431 if(!pid)
432 {
433 /* Child process fills the pipe */
434 int pos;
435 struct timeval timeout;
436 fd_set fdset;
437
438 buffer = (unsigned char *) malloc(CD_FRAMESIZE_RAW0);
439
440 for(pos = toc.cdte_addr.lba; vcd_read(fd, pos, buffer) >= 0; pos ++)
441 {
442 if(kill(parent, 0) < 0) break;
443
444 FD_ZERO(&fdset);
445 FD_SET(pipe_fd[1], &fdset);
446 timeout.tv_sec = 10;
447 timeout.tv_usec = 0;
448 if(select(pipe_fd[1]+1, NULL, &fdset, NULL, &timeout) <= 0) break;
449 if(write(pipe_fd[1], buffer, CD_FRAMESIZE_RAW0) < 0) break;
450 }
451
452 free(buffer);
453 exit(0);
454 }
455
456 return(pipe_fd[0]);
457 }
458 #endif
459
460 typedef struct
461 {
462 SMPEG_Frame *frame;
463 int frameCount;
464 SDL_mutex *lock;
465 } update_context;
466
update(void * data,SMPEG_Frame * frame)467 void update(void *data, SMPEG_Frame *frame)
468 {
469 update_context *context = (update_context *)data;
470 context->frame = frame;
471 ++context->frameCount;
472 }
473
474 /* Flag telling the UI that the movie or song should be skipped */
475 int done;
476
next_movie(int sig)477 void next_movie(int sig)
478 {
479 done = 1;
480 }
481
main(int argc,char * argv[])482 int main(int argc, char *argv[])
483 {
484 int use_audio, use_video;
485 int fullscreen;
486 int scalesize;
487 int scale_width, scale_height;
488 int loop_play;
489 int i, pause;
490 int volume;
491 Uint32 seek;
492 float skip;
493 int filtering;
494 SDL_Window *window = NULL;
495 SDL_Renderer *renderer = NULL;
496 SDL_Texture *texture = NULL;
497 int texture_width;
498 int texture_height;
499 update_context context;
500 int frameCount = 0;
501 SMPEG *mpeg;
502 SMPEG_Info info;
503 char *basefile;
504 const char *title = NULL;
505 SDL_version sdlver;
506 SMPEG_version smpegver;
507 int fd;
508 int status;
509
510 /* Get the command line options */
511 use_audio = 1;
512 use_video = 1;
513 fullscreen = 0;
514 scalesize = 1;
515 scale_width = 0;
516 scale_height = 0;
517 loop_play = 0;
518 volume = 100;
519 seek = 0;
520 skip = 0;
521 filtering = 0;
522 fd = 0;
523 for ( i=1; argv[i] && (argv[i][0] == '-') && (argv[i][1] != 0); ++i ) {
524 if ( (strcmp(argv[i], "--noaudio") == 0) ||
525 (strcmp(argv[i], "--nosound") == 0) ) {
526 use_audio = 0;
527 } else
528 if ( strcmp(argv[i], "--novideo") == 0 ) {
529 use_video = 0;
530 } else
531 if ( strcmp(argv[i], "--fullscreen") == 0 ) {
532 fullscreen = 1;
533 } else
534 if ((strcmp(argv[i], "--double") == 0)||(strcmp(argv[i], "-2") == 0)) {
535 scalesize = 2;
536 } else
537 if ((strcmp(argv[i], "--loop") == 0) || (strcmp(argv[i], "-l") == 0)) {
538 loop_play = 1;
539 } else
540 if ( strcmp(argv[i], "--bilinear") == 0 ) {
541 filtering = 1;
542 } else
543 if ((strcmp(argv[i], "--seek") == 0)||(strcmp(argv[i], "-S") == 0)) {
544 ++i;
545 if ( argv[i] ) {
546 seek = atol(argv[i]);
547 }
548 } else
549 if ((strcmp(argv[i], "--skip") == 0)||(strcmp(argv[i], "-k") == 0)) {
550 ++i;
551 if ( argv[i] ) {
552 skip = (float)atof(argv[i]);
553 }
554 } else
555 if ((strcmp(argv[i], "--volume") == 0)||(strcmp(argv[i], "-v") == 0)) {
556 ++i;
557 if (i >= argc)
558 {
559 fprintf(stderr, "Please specify volume when using --volume or -v\n");
560 return(1);
561 }
562 if ( argv[i] ) {
563 volume = atoi(argv[i]);
564 }
565 if ( ( volume < 0 ) || ( volume > 100 ) ) {
566 fprintf(stderr, "Volume must be between 0 and 100\n");
567 volume = 100;
568 }
569 } else
570 if ((strcmp(argv[i], "--title") == 0)||(strcmp(argv[i], "-t") == 0)) {
571 ++i;
572 if (i >= argc)
573 {
574 fprintf(stderr, "Please specify title when using --title or -t\n");
575 return(1);
576 }
577 if ( argv[i] ) {
578 title = argv[i];
579 }
580 } else
581 if ((strcmp(argv[i], "--version") == 0) ||
582 (strcmp(argv[i], "-V") == 0)) {
583 SDL_GetVersion(&sdlver);
584 SMPEG_VERSION(&smpegver);
585 printf("SDL version: %d.%d.%d\n"
586 "SMPEG version: %d.%d.%d\n",
587 sdlver.major, sdlver.minor, sdlver.patch,
588 smpegver.major, smpegver.minor, smpegver.patch);
589 return(0);
590 } else
591 if ((strcmp(argv[i], "--scale") == 0)||(strcmp(argv[i], "-s") == 0)) {
592 ++i;
593 if ( argv[i] ) {
594 sscanf(argv[i], "%dx%d", &scale_width, &scale_height);
595 }
596 } else
597 if ((strcmp(argv[i], "--help") == 0) || (strcmp(argv[i], "-h") == 0)) {
598 usage(argv[0]);
599 return(0);
600 } else {
601 fprintf(stderr, "Warning: Unknown option: %s\n", argv[i]);
602 }
603 }
604 /* If there were no arguments just print the usage */
605 if (argc == 1) {
606 usage(argv[0]);
607 return(0);
608 }
609
610 #if defined(linux) || defined(__FreeBSD__) /* Plaympeg doesn't need a mouse */
611 putenv("SDL_NOMOUSE=1");
612 #endif
613
614 context.lock = SDL_CreateMutex();
615
616 /* Play the mpeg files! */
617 status = 0;
618 for ( ; argv[i]; ++i ) {
619 /* Initialize SDL */
620 if ( use_video ) {
621 if (SDL_Init(SDL_INIT_VIDEO) < 0) {
622 fprintf(stderr, "Warning: Couldn't init SDL video: %s\n",
623 SDL_GetError());
624 fprintf(stderr, "Will ignore video stream\n");
625 use_video = 0;
626 }
627 }
628
629 if ( use_audio ) {
630 if (SDL_Init(SDL_INIT_AUDIO) < 0) {
631 fprintf(stderr, "Warning: Couldn't init SDL audio: %s\n",
632 SDL_GetError());
633 fprintf(stderr, "Will ignore audio stream\n");
634 use_audio = 0;
635 }
636 }
637
638 /* Allow Ctrl-C when there's no video output */
639 signal(SIGINT, next_movie);
640
641 /* Create the MPEG stream */
642 #ifdef NET_SUPPORT
643 #ifdef RAW_SUPPORT
644 /* Check if source is an IP address and port*/
645 if((fd = raw_open(argv[i])) != 0)
646 mpeg = SMPEG_new_descr(fd, &info, use_audio);
647 else
648 #endif
649 #ifdef HTTP_SUPPORT
650 /* Check if source is an http URL */
651 if((fd = http_open(argv[i])) != 0)
652 mpeg = SMPEG_new_descr(fd, &info, use_audio);
653 else
654 #endif
655 #ifdef FTP_SUPPORT
656 /* Check if source is an http URL */
657 if((fd = ftp_open(argv[i])) != 0)
658 mpeg = SMPEG_new_descr(fd, &info, use_audio);
659 else
660 #endif
661 #endif
662 #ifdef VCD_SUPPORT
663 /* Check if source is a CDROM device */
664 if((fd = vcd_open(argv[i])) != 0)
665 mpeg = SMPEG_new_descr(fd, &info, use_audio);
666 else
667 #endif
668 {
669 if(strcmp(argv[i], "-") == 0) /* Use stdin for input */
670 mpeg = SMPEG_new_descr(0, &info, use_audio);
671 else
672 mpeg = SMPEG_new(argv[i], &info, use_audio);
673 }
674
675 if ( SMPEG_error(mpeg) ) {
676 fprintf(stderr, "%s: %s\n", argv[i], SMPEG_error(mpeg));
677 SMPEG_delete(mpeg);
678 status = -1;
679 continue;
680 }
681 SMPEG_enableaudio(mpeg, use_audio);
682 SMPEG_enablevideo(mpeg, use_video);
683 SMPEG_setvolume(mpeg, volume);
684
685 /* Print information about the video */
686 basefile = strrchr(argv[i], '/');
687 if ( basefile ) {
688 ++basefile;
689 } else {
690 basefile = argv[i];
691 }
692 if ( info.has_audio && info.has_video ) {
693 printf("%s: MPEG system stream (audio/video)\n", basefile);
694 } else if ( info.has_audio ) {
695 printf("%s: MPEG audio stream\n", basefile);
696 } else if ( info.has_video ) {
697 printf("%s: MPEG video stream\n", basefile);
698 }
699 if ( info.has_video ) {
700 printf("\tVideo %dx%d resolution\n", info.width, info.height);
701 }
702 if ( info.has_audio ) {
703 printf("\tAudio %s\n", info.audio_string);
704 }
705 if ( info.total_size ) {
706 printf("\tSize: %d\n", info.total_size);
707 }
708 if ( info.total_time ) {
709 printf("\tTotal time: %f\n", info.total_time);
710 }
711
712 /* Set up video display if needed */
713 if ( info.has_video && use_video ) {
714 Uint32 window_flags;
715 int width, height;
716
717 if ( scale_width ) {
718 width = scale_width;
719 } else {
720 width = info.width;
721 }
722 width *= scalesize;
723 if ( scale_height ) {
724 height = scale_height;
725 } else {
726 height = info.height;
727 }
728 height *= scalesize;
729 window_flags = SDL_WINDOW_RESIZABLE;
730 if ( fullscreen ) {
731 window_flags |= SDL_WINDOW_FULLSCREEN;
732 }
733 if (window) {
734 SDL_SetWindowSize(window, width, height);
735 SDL_DestroyTexture(texture);
736 } else {
737 window = SDL_CreateWindow(title ? title : argv[i],
738 SDL_WINDOWPOS_CENTERED,
739 SDL_WINDOWPOS_CENTERED,
740 width, height, window_flags);
741 if ( window == NULL ) {
742 fprintf(stderr, "Unable to create %dx%d window: %s\n",
743 width, height, SDL_GetError());
744 continue;
745 }
746 renderer = SDL_CreateRenderer(window, -1, 0);
747 }
748 if ( fullscreen ) {
749 SDL_ShowCursor(0);
750 }
751
752 if (filtering) {
753 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
754 } else {
755 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
756 }
757
758 texture_width = (info.width + 15) & ~15;
759 texture_height = (info.height + 15) & ~15;
760 texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, texture_width, texture_height);
761
762 context.frameCount = frameCount = 0;
763 SMPEG_setdisplay(mpeg, update, &context, context.lock);
764 } else {
765 SDL_QuitSubSystem(SDL_INIT_VIDEO);
766 use_video = 0;
767 }
768
769 /* Set any special playback parameters */
770 if ( loop_play ) {
771 SMPEG_loop(mpeg, 1);
772 }
773
774 /* Seek starting position */
775 if(seek) SMPEG_seek(mpeg, seek);
776
777 /* Skip seconds to starting position */
778 if(skip) SMPEG_skip(mpeg, skip);
779
780 /* Play it, and wait for playback to complete */
781 SMPEG_play(mpeg);
782 done = 0;
783 pause = 0;
784 while ( ! done && ( pause || (SMPEG_status(mpeg) == SMPEG_PLAYING) ) ) {
785 SDL_Event event;
786
787 while ( use_video && SDL_PollEvent(&event) ) {
788 switch (event.type) {
789 case SDL_WINDOWEVENT:
790 if ( event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED ) {
791 SDL_RenderSetViewport(renderer, NULL);
792 }
793 break;
794 case SDL_KEYDOWN:
795 if ( (event.key.keysym.sym == SDLK_ESCAPE) || (event.key.keysym.sym == SDLK_q) ) {
796 // Quit
797 done = 1;
798 } else if ( event.key.keysym.sym == SDLK_RETURN ) {
799 // toggle fullscreen
800 if ( event.key.keysym.mod & KMOD_ALT ) {
801 fullscreen = !fullscreen;
802 if (SDL_SetWindowFullscreen(window, fullscreen ? SDL_WINDOW_FULLSCREEN : 0) < 0) {
803 fullscreen = !fullscreen;
804 }
805 SDL_ShowCursor(!fullscreen);
806 }
807 } else if ( event.key.keysym.sym == SDLK_UP ) {
808 // Volume up
809 if ( volume < 100 ) {
810 if ( event.key.keysym.mod & KMOD_SHIFT ) { // 10+
811 volume += 10;
812 } else if ( event.key.keysym.mod & KMOD_CTRL ) { // 100+
813 volume = 100;
814 } else { // 1+
815 volume++;
816 }
817 if ( volume > 100 )
818 volume = 100;
819 SMPEG_setvolume(mpeg, volume);
820 }
821 } else if ( event.key.keysym.sym == SDLK_DOWN ) {
822 // Volume down
823 if ( volume > 0 ) {
824 if ( event.key.keysym.mod & KMOD_SHIFT ) {
825 volume -= 10;
826 } else if ( event.key.keysym.mod & KMOD_CTRL ) {
827 volume = 0;
828 } else {
829 volume--;
830 }
831 if ( volume < 0 )
832 volume = 0;
833 SMPEG_setvolume(mpeg, volume);
834 }
835 } else if ( event.key.keysym.sym == SDLK_PAGEUP ) {
836 // Full volume
837 volume = 100;
838 SMPEG_setvolume(mpeg, volume);
839 } else if ( event.key.keysym.sym == SDLK_PAGEDOWN ) {
840 // Volume off
841 volume = 0;
842 SMPEG_setvolume(mpeg, volume);
843 } else if ( event.key.keysym.sym == SDLK_SPACE ) {
844 // Toggle play / pause
845 if ( SMPEG_status(mpeg) == SMPEG_PLAYING ) {
846 SMPEG_pause(mpeg);
847 pause = 1;
848 } else {
849 SMPEG_play(mpeg);
850 pause = 0;
851 }
852 } else if ( event.key.keysym.sym == SDLK_RIGHT ) {
853 // Forward
854 if ( event.key.keysym.mod & KMOD_SHIFT ) {
855 SMPEG_skip(mpeg, 100);
856 } else if ( event.key.keysym.mod & KMOD_CTRL ) {
857 SMPEG_skip(mpeg, 50);
858 } else {
859 SMPEG_skip(mpeg, 5);
860 }
861 } else if ( event.key.keysym.sym == SDLK_LEFT ) {
862 // Reverse
863 if ( event.key.keysym.mod & KMOD_SHIFT ) {
864
865 } else if ( event.key.keysym.mod & KMOD_CTRL ) {
866
867 } else {
868
869 }
870 } else if ( event.key.keysym.sym == SDLK_KP_MINUS ) {
871 // Scale minus
872 if ( scalesize > 1 ) {
873 scalesize--;
874 }
875 } else if ( event.key.keysym.sym == SDLK_KP_PLUS ) {
876 // Scale plus
877 scalesize++;
878 }
879 break;
880 case SDL_QUIT:
881 done = 1;
882 break;
883 default:
884 break;
885 }
886 }
887
888 if (use_video && context.frameCount > frameCount) {
889 SDL_Rect src;
890
891 SDL_mutexP(context.lock);
892 SDL_assert(context.frame->image_width == texture_width);
893 SDL_assert(context.frame->image_height == texture_height);
894 SDL_UpdateTexture(texture, NULL, context.frame->image, context.frame->image_width);
895 frameCount = context.frameCount;
896 SDL_mutexV(context.lock);
897
898 src.x = 0;
899 src.y = 0;
900 src.w = info.width;
901 src.h = info.height;
902 SDL_RenderCopy(renderer, texture, &src, NULL);
903
904 SDL_RenderPresent(renderer);
905 } else {
906 SDL_Delay(0);
907 }
908 }
909
910 SMPEG_delete(mpeg);
911 }
912 SDL_DestroyMutex(context.lock);
913 SDL_Quit();
914
915 #if defined(RAW_SUPPORT) || defined(HTTP_SUPPORT) || defined(FTP_SUPPORT) || \
916 defined(VCD_SUPPORT)
917 if(fd) close(fd);
918 #endif
919
920 return(status);
921 }
922