break cmd variable into exited and paused
[fbff.git] / fbff.c
blobda19b407a5df03af56c2c3b34e6c3934f2889ad3
1 /*
2 * fbff - a small ffmpeg-based framebuffer/oss media player
4 * Copyright (C) 2009-2011 Ali Gholami Rudi
6 * This program is released under GNU GPL version 2.
7 */
8 #include <fcntl.h>
9 #include <pty.h>
10 #include <ctype.h>
11 #include <signal.h>
12 #include <stdint.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <termios.h>
17 #include <unistd.h>
18 #include <sys/poll.h>
19 #include <sys/soundcard.h>
20 #include <pthread.h>
21 #include "config.h"
22 #include "ffs.h"
23 #include "draw.h"
25 #define MIN(a, b) ((a) < (b) ? (a) : (b))
26 #define MAX(a, b) ((a) > (b) ? (a) : (b))
28 static int frame_jmp = 1; /* the changes to pos_cur for each frame */
29 static int afd; /* oss fd */
31 static int arg;
32 static struct termios termios;
33 static int paused;
34 static int exited;
36 static float zoom = 1;
37 static int magnify = 0;
38 static int jump = 0;
39 static int fullscreen = 0;
40 static int audio = 1;
41 static int video = 1;
42 static int just = 0;
44 static struct ffs *affs; /* audio ffmpeg stream */
45 static struct ffs *vffs; /* video ffmpeg stream */
47 static void draw_frame(void *img, int linelen)
49 int w, h;
50 fbval_t buf[1 << 14];
51 int nr, nc, cb;
52 int i, r, c;
53 ffs_vinfo(vffs, &w, &h);
54 nr = MIN(h * zoom, fb_rows() / magnify);
55 nc = MIN(w * zoom, fb_cols() / magnify);
56 cb = just ? fb_cols() - nc * magnify : 0;
57 for (r = 0; r < nr; r++) {
58 fbval_t *row = img + r * linelen;
59 if (magnify == 1) {
60 fb_set(r, cb, row, nc);
61 continue;
63 for (c = 0; c < nc; c++)
64 for (i = 0; i < magnify; i++)
65 buf[c * magnify + i] = row[c];
66 for (i = 0; i < magnify; i++)
67 fb_set(r * magnify + i, cb, buf, nc * magnify);
71 #define ABUFSZ (1 << 18)
73 static int a_cons;
74 static int a_prod;
75 static char a_buf[AUDIOBUFS][ABUFSZ];
76 static int a_len[AUDIOBUFS];
77 static int a_reset;
79 static int a_conswait(void)
81 return a_cons == a_prod;
84 static int a_prodwait(void)
86 return ((a_prod + 1) & (AUDIOBUFS - 1)) == a_cons;
89 static void a_doreset(int pause)
91 a_reset = 1 + pause;
92 while (audio && a_reset)
93 usleep(1000);
96 static int readkey(void)
98 char b;
99 if (read(STDIN_FILENO, &b, 1) <= 0)
100 return -1;
101 return b;
104 static void waitkey(void)
106 struct pollfd ufds[1];
107 ufds[0].fd = STDIN_FILENO;
108 ufds[0].events = POLLIN;
109 poll(ufds, 1, -1);
112 static int ffarg(void)
114 int n = arg;
115 arg = 0;
116 return n ? n : 1;
119 static void ffjmp(int n, int rel)
121 struct ffs *ffs = video ? vffs : affs;
122 long pos = ffs_pos(ffs, n);
123 a_doreset(0);
124 if (audio)
125 ffs_seek(affs, pos, frame_jmp);
126 if (video)
127 ffs_seek(vffs, pos, frame_jmp);
130 static void printinfo(void)
132 struct ffs *ffs = video ? vffs : affs;
133 printf("fbff: %8lx\r", ffs_pos(ffs, 0));
134 fflush(stdout);
137 #define JMP1 (1 << 5)
138 #define JMP2 (JMP1 << 3)
139 #define JMP3 (JMP2 << 5)
141 static void execkey(void)
143 int c;
144 while ((c = readkey()) != -1) {
145 switch (c) {
146 case 'q':
147 exited = 1;
148 break;
149 case 'l':
150 ffjmp(ffarg() * JMP1, 1);
151 break;
152 case 'h':
153 ffjmp(-ffarg() * JMP1, 1);
154 break;
155 case 'j':
156 ffjmp(ffarg() * JMP2, 1);
157 break;
158 case 'k':
159 ffjmp(-ffarg() * JMP2, 1);
160 break;
161 case 'J':
162 ffjmp(ffarg() * JMP3, 1);
163 break;
164 case 'K':
165 ffjmp(-ffarg() * JMP3, 1);
166 break;
167 case '%':
168 if (arg)
169 ffjmp(100, 0);
170 break;
171 case 'i':
172 printinfo();
173 break;
174 case ' ':
175 case 'p':
176 paused = !paused;
177 break;
178 case 27:
179 arg = 0;
180 break;
181 default:
182 if (isdigit(c))
183 arg = arg * 10 + c - '0';
188 static int is_vsync(void)
190 int cur = ffs_seq(affs, 0);
191 int all = ffs_seq(affs, 1);
192 int ratio = all ? (all - cur) * 1024 / all : 512;
193 int avdiff = AUDIOBUFS * 4 * ratio / 1024;
194 return ffs_seq(vffs, 1) + avdiff < ffs_seq(affs, 1);
197 static void mainloop(void)
199 int eof = 0;
200 while (!exited) {
201 execkey();
202 if (paused) {
203 a_doreset(1);
204 waitkey();
205 continue;
207 while (audio && !eof && !a_prodwait()) {
208 int ret = ffs_adec(affs, a_buf[a_prod], ABUFSZ);
209 if (ret < 0)
210 eof = 1;
211 if (ret > 0) {
212 a_len[a_prod] = ret;
213 a_prod = (a_prod + 1) & (AUDIOBUFS - 1);
216 if (video && (!audio || eof || is_vsync())) {
217 int ignore = jump && !(ffs_seq(vffs, 0) % (jump + 1));
218 char *buf;
219 int ret = ffs_vdec(vffs, ignore ? NULL : &buf);
220 if (ret < 0)
221 break;
222 if (ret > 0)
223 draw_frame((void *) buf, ret);
224 ffs_wait(vffs);
227 exited = 1;
230 static void oss_init(void)
232 int rate, ch, bps;
233 afd = open("/dev/dsp", O_RDWR);
234 if (afd < 0) {
235 fprintf(stderr, "cannot open /dev/dsp\n");
236 exit(1);
238 ffs_ainfo(affs, &rate, &bps, &ch);
239 ioctl(afd, SOUND_PCM_WRITE_RATE, &rate);
240 ioctl(afd, SOUND_PCM_WRITE_CHANNELS, &ch);
241 ioctl(afd, SOUND_PCM_WRITE_BITS, &bps);
244 static void oss_close(void)
246 close(afd);
249 static void *process_audio(void *dat)
251 oss_init();
252 while (1) {
253 while (!a_reset && a_conswait() && !exited)
254 usleep(1000);
255 if (exited)
256 goto ret;
257 if (a_reset) {
258 if (a_reset == 1)
259 a_cons = a_prod;
260 a_reset = 0;
261 continue;
263 write(afd, a_buf[a_cons], a_len[a_cons]);
264 a_cons = (a_cons + 1) & (AUDIOBUFS - 1);
266 ret:
267 oss_close();
268 return NULL;
271 static void term_setup(void)
273 struct termios newtermios;
274 tcgetattr(STDIN_FILENO, &termios);
275 newtermios = termios;
276 newtermios.c_lflag &= ~ICANON;
277 newtermios.c_lflag &= ~ECHO;
278 tcsetattr(STDIN_FILENO, TCSAFLUSH, &newtermios);
279 fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | O_NONBLOCK);
282 static void term_cleanup(void)
284 tcsetattr(STDIN_FILENO, 0, &termios);
287 static void sigcont(int sig)
289 term_setup();
292 static char *usage = "usage: fbff [options] file\n"
293 "\noptions:\n"
294 " -m x magnify the screen by repeating pixels\n"
295 " -z x zoom the screen using ffmpeg\n"
296 " -j x jump every x video frames; for slow machines\n"
297 " -f start full screen\n"
298 " -v video only playback\n"
299 " -a audio only playback\n"
300 " -t use time based seeking; only if the default does't work\n"
301 " -R adjust the video to the right of the screen\n\n";
303 static void read_args(int argc, char *argv[])
305 int i = 1;
306 while (i < argc) {
307 if (!strcmp(argv[i], "-m"))
308 magnify = atoi(argv[++i]);
309 if (!strcmp(argv[i], "-z"))
310 zoom = atof(argv[++i]);
311 if (!strcmp(argv[i], "-j"))
312 jump = atoi(argv[++i]);
313 if (!strcmp(argv[i], "-f"))
314 fullscreen = 1;
315 if (!strcmp(argv[i], "-a"))
316 video = 0;
317 if (!strcmp(argv[i], "-v"))
318 audio = 0;
319 if (!strcmp(argv[i], "-t"))
320 frame_jmp = 1024 / 32;
321 if (!strcmp(argv[i], "-h"))
322 printf(usage);
323 if (!strcmp(argv[i], "-R"))
324 just = 1;
325 i++;
329 int main(int argc, char *argv[])
331 pthread_t a_thread;
332 char *path = argv[argc - 1];
333 if (argc < 2) {
334 printf("usage: %s [options] filename\n", argv[0]);
335 return 1;
337 read_args(argc, argv);
338 ffs_globinit();
339 if (video && !(vffs = ffs_alloc(path, 1)))
340 video = 0;
341 if (audio && !(affs = ffs_alloc(path, 0)))
342 audio = 0;
343 if (!video && !audio)
344 return 1;
345 if (audio)
346 pthread_create(&a_thread, NULL, process_audio, NULL);
347 if (video) {
348 int w, h;
349 if (fb_init())
350 return 1;
351 ffs_vinfo(vffs, &w, &h);
352 if (!magnify)
353 magnify = MAX(1, fb_cols() / w / zoom);
354 if (fullscreen)
355 zoom = (float) fb_cols() / w / magnify;
356 ffs_vsetup(vffs, zoom, fb_mode());
358 term_setup();
359 signal(SIGCONT, sigcont);
360 mainloop();
361 term_cleanup();
363 if (video) {
364 fb_free();
365 ffs_free(vffs);
367 if (audio) {
368 pthread_join(a_thread, NULL);
369 ffs_free(affs);
371 return 0;