fbff: change -m default to 1
[fbff.git] / fbff.c
blob79e7a5d77206459c07f0027e640fd7e0023eb0bc
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 = 1;
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 (1) {
201 execkey();
202 if (exited)
203 break;
204 if (paused) {
205 a_doreset(1);
206 waitkey();
207 continue;
209 while (audio && !eof && !a_prodwait()) {
210 int ret = ffs_adec(affs, a_buf[a_prod], ABUFSZ);
211 if (ret < 0)
212 eof = 1;
213 if (ret > 0) {
214 a_len[a_prod] = ret;
215 a_prod = (a_prod + 1) & (AUDIOBUFS - 1);
218 if (video && (!audio || eof || is_vsync())) {
219 int ignore = jump && !(ffs_seq(vffs, 0) % (jump + 1));
220 char *buf;
221 int ret = ffs_vdec(vffs, ignore ? NULL : &buf);
222 if (ret < 0)
223 break;
224 if (ret > 0)
225 draw_frame((void *) buf, ret);
226 ffs_wait(vffs);
229 exited = 1;
232 static void oss_init(void)
234 int rate, ch, bps;
235 afd = open("/dev/dsp", O_RDWR);
236 if (afd < 0) {
237 fprintf(stderr, "cannot open /dev/dsp\n");
238 exit(1);
240 ffs_ainfo(affs, &rate, &bps, &ch);
241 ioctl(afd, SOUND_PCM_WRITE_RATE, &rate);
242 ioctl(afd, SOUND_PCM_WRITE_CHANNELS, &ch);
243 ioctl(afd, SOUND_PCM_WRITE_BITS, &bps);
246 static void oss_close(void)
248 close(afd);
251 static void *process_audio(void *dat)
253 oss_init();
254 while (1) {
255 while (!a_reset && a_conswait() && !exited)
256 usleep(1000);
257 if (exited)
258 goto ret;
259 if (a_reset) {
260 if (a_reset == 1)
261 a_cons = a_prod;
262 a_reset = 0;
263 continue;
265 write(afd, a_buf[a_cons], a_len[a_cons]);
266 a_cons = (a_cons + 1) & (AUDIOBUFS - 1);
268 ret:
269 oss_close();
270 return NULL;
273 static void term_setup(void)
275 struct termios newtermios;
276 tcgetattr(STDIN_FILENO, &termios);
277 newtermios = termios;
278 newtermios.c_lflag &= ~ICANON;
279 newtermios.c_lflag &= ~ECHO;
280 tcsetattr(STDIN_FILENO, TCSAFLUSH, &newtermios);
281 fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | O_NONBLOCK);
284 static void term_cleanup(void)
286 tcsetattr(STDIN_FILENO, 0, &termios);
289 static void sigcont(int sig)
291 term_setup();
294 static char *usage = "usage: fbff [options] file\n"
295 "\noptions:\n"
296 " -m x magnify the screen by repeating pixels\n"
297 " -z x zoom the screen using ffmpeg\n"
298 " -j x jump every x video frames; for slow machines\n"
299 " -f start full screen\n"
300 " -v video only playback\n"
301 " -a audio only playback\n"
302 " -t use time based seeking; only if the default does't work\n"
303 " -R adjust the video to the right of the screen\n\n";
305 static void read_args(int argc, char *argv[])
307 int i = 1;
308 while (i < argc) {
309 if (!strcmp(argv[i], "-m"))
310 magnify = atoi(argv[++i]);
311 if (!strcmp(argv[i], "-z"))
312 zoom = atof(argv[++i]);
313 if (!strcmp(argv[i], "-j"))
314 jump = atoi(argv[++i]);
315 if (!strcmp(argv[i], "-f"))
316 fullscreen = 1;
317 if (!strcmp(argv[i], "-a"))
318 video = 0;
319 if (!strcmp(argv[i], "-v"))
320 audio = 0;
321 if (!strcmp(argv[i], "-t"))
322 frame_jmp = 1024 / 32;
323 if (!strcmp(argv[i], "-h"))
324 printf(usage);
325 if (!strcmp(argv[i], "-R"))
326 just = 1;
327 i++;
331 int main(int argc, char *argv[])
333 pthread_t a_thread;
334 char *path = argv[argc - 1];
335 if (argc < 2) {
336 printf("usage: %s [options] filename\n", argv[0]);
337 return 1;
339 read_args(argc, argv);
340 ffs_globinit();
341 if (video && !(vffs = ffs_alloc(path, 1)))
342 video = 0;
343 if (audio && !(affs = ffs_alloc(path, 0)))
344 audio = 0;
345 if (!video && !audio)
346 return 1;
347 if (audio)
348 pthread_create(&a_thread, NULL, process_audio, NULL);
349 if (video) {
350 int w, h;
351 if (fb_init())
352 return 1;
353 ffs_vinfo(vffs, &w, &h);
354 if (magnify != 1 && sizeof(fbval_t) != FBM_BPP(fb_mode()))
355 fprintf(stderr, "fbff: magnify != 1 and fbval_t doesn't match\n");
356 if (fullscreen)
357 zoom = (float) fb_cols() / w / magnify;
358 ffs_vsetup(vffs, zoom, fb_mode());
360 term_setup();
361 signal(SIGCONT, sigcont);
362 mainloop();
363 term_cleanup();
365 if (video) {
366 fb_free();
367 ffs_free(vffs);
369 if (audio) {
370 pthread_join(a_thread, NULL);
371 ffs_free(affs);
373 return 0;