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