README added
[fbff.git] / fbff.c
blobae6834be753dc48f2de12151fca5c11c373bf72a
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 arg;
29 static struct termios termios;
30 static int paused;
31 static int exited;
33 static float zoom = 1;
34 static int magnify = 1;
35 static int jump = 0;
36 static int fullscreen = 0;
37 static int audio = 1;
38 static int video = 1;
39 static int just = 0;
40 static int frame_jmp = 1; /* the changes to pos_cur for each frame */
42 static struct ffs *affs; /* audio ffmpeg stream */
43 static struct ffs *vffs; /* video ffmpeg stream */
44 static int afd; /* oss fd */
45 static int vnum; /* decoded video frame count */
47 static int sync_diff; /* audio/video frame position diff */
48 static int sync_cnt; /* synchronize if nonzero */
50 static void stroll(void)
52 usleep(10000);
55 static void draw_frame(void *img, int linelen)
57 int w, h;
58 fbval_t buf[1 << 14];
59 int nr, nc, cb;
60 int i, r, c;
61 ffs_vinfo(vffs, &w, &h);
62 nr = MIN(h * zoom, fb_rows() / magnify);
63 nc = MIN(w * zoom, fb_cols() / magnify);
64 cb = just ? fb_cols() - nc * magnify : 0;
65 for (r = 0; r < nr; r++) {
66 fbval_t *row = img + r * linelen;
67 if (magnify == 1) {
68 fb_set(r, cb, row, nc);
69 continue;
71 for (c = 0; c < nc; c++)
72 for (i = 0; i < magnify; i++)
73 buf[c * magnify + i] = row[c];
74 for (i = 0; i < magnify; i++)
75 fb_set(r * magnify + i, cb, buf, nc * magnify);
79 #define ABUFSZ (1 << 18)
81 static int a_cons;
82 static int a_prod;
83 static char a_buf[AUDIOBUFS][ABUFSZ];
84 static int a_len[AUDIOBUFS];
85 static int a_reset;
87 static int a_conswait(void)
89 return a_cons == a_prod;
92 static int a_prodwait(void)
94 return ((a_prod + 1) & (AUDIOBUFS - 1)) == a_cons;
97 static void a_doreset(int pause)
99 a_reset = 1 + pause;
100 while (audio && a_reset)
101 stroll();
104 static int readkey(void)
106 char b;
107 if (read(STDIN_FILENO, &b, 1) <= 0)
108 return -1;
109 return b;
112 static void waitkey(void)
114 struct pollfd ufds[1];
115 ufds[0].fd = STDIN_FILENO;
116 ufds[0].events = POLLIN;
117 poll(ufds, 1, -1);
120 static int ffarg(int def)
122 int n = arg;
123 arg = 0;
124 return n ? n : def;
127 static void ffjmp(int n, int rel)
129 struct ffs *ffs = video ? vffs : affs;
130 long pos = ffs_pos(ffs, n);
131 a_doreset(0);
132 if (audio)
133 ffs_seek(affs, pos, frame_jmp);
134 if (video)
135 ffs_seek(vffs, pos, frame_jmp);
138 static void printinfo(void)
140 struct ffs *ffs = video ? vffs : affs;
141 printf("fbff: %8lx \t (AV: %d)\r",
142 ffs_pos(ffs, 0), ffs_avdiff(vffs, affs));
143 fflush(stdout);
146 #define JMP1 (1 << 5)
147 #define JMP2 (JMP1 << 3)
148 #define JMP3 (JMP2 << 5)
150 static void execkey(void)
152 int c;
153 while ((c = readkey()) != -1) {
154 switch (c) {
155 case 'q':
156 exited = 1;
157 break;
158 case 'l':
159 ffjmp(ffarg(1) * JMP1, 1);
160 break;
161 case 'h':
162 ffjmp(-ffarg(1) * JMP1, 1);
163 break;
164 case 'j':
165 ffjmp(ffarg(1) * JMP2, 1);
166 break;
167 case 'k':
168 ffjmp(-ffarg(1) * JMP2, 1);
169 break;
170 case 'J':
171 ffjmp(ffarg(1) * JMP3, 1);
172 break;
173 case 'K':
174 ffjmp(-ffarg(1) * JMP3, 1);
175 break;
176 case '%':
177 if (arg)
178 ffjmp(100, 0);
179 break;
180 case 'i':
181 printinfo();
182 break;
183 case ' ':
184 case 'p':
185 paused = !paused;
186 sync_cnt = 10;
187 break;
188 case '-':
189 sync_diff = -ffarg(0);
190 break;
191 case '+':
192 sync_diff = ffarg(0);
193 break;
194 case 'a':
195 sync_diff = ffs_avdiff(vffs, affs);
196 break;
197 case 's':
198 sync_cnt = ffarg(10);
199 break;
200 case 27:
201 arg = 0;
202 break;
203 default:
204 if (isdigit(c))
205 arg = arg * 10 + c - '0';
210 /* return nonzero if one more video frame can be decoded */
211 static int vsync(void)
213 if (sync_cnt > 0) {
214 sync_cnt--;
215 return ffs_avdiff(vffs, affs) >= sync_diff;
217 ffs_wait(vffs);
218 return 1;
221 static void mainloop(void)
223 int eof = 0;
224 while (eof < audio + video) {
225 execkey();
226 if (exited)
227 break;
228 if (paused) {
229 a_doreset(1);
230 waitkey();
231 continue;
233 while (audio && !eof && !a_prodwait()) {
234 int ret = ffs_adec(affs, a_buf[a_prod], ABUFSZ);
235 if (ret < 0)
236 eof++;
237 if (ret > 0) {
238 a_len[a_prod] = ret;
239 a_prod = (a_prod + 1) & (AUDIOBUFS - 1);
242 if (video && (!audio || eof || vsync())) {
243 int ignore = jump && (vnum++ % (jump + 1));
244 void *buf;
245 int ret = ffs_vdec(vffs, ignore ? NULL : &buf);
246 if (ret < 0)
247 eof++;
248 if (ret > 0)
249 draw_frame((void *) buf, ret);
250 } else {
251 stroll();
254 exited = 1;
257 static void oss_init(void)
259 int rate, ch, bps;
260 afd = open("/dev/dsp", O_RDWR);
261 if (afd < 0) {
262 fprintf(stderr, "cannot open /dev/dsp\n");
263 exit(1);
265 ffs_ainfo(affs, &rate, &bps, &ch);
266 ioctl(afd, SOUND_PCM_WRITE_RATE, &rate);
267 ioctl(afd, SOUND_PCM_WRITE_CHANNELS, &ch);
268 ioctl(afd, SOUND_PCM_WRITE_BITS, &bps);
271 static void oss_close(void)
273 close(afd);
276 static void *process_audio(void *dat)
278 oss_init();
279 while (1) {
280 while (!a_reset && (a_conswait() || paused) && !exited)
281 stroll();
282 if (exited)
283 goto ret;
284 if (a_reset) {
285 if (a_reset == 1)
286 a_cons = a_prod;
287 a_reset = 0;
288 continue;
290 write(afd, a_buf[a_cons], a_len[a_cons]);
291 a_cons = (a_cons + 1) & (AUDIOBUFS - 1);
293 ret:
294 oss_close();
295 return NULL;
298 static void term_setup(void)
300 struct termios newtermios;
301 tcgetattr(STDIN_FILENO, &termios);
302 newtermios = termios;
303 newtermios.c_lflag &= ~ICANON;
304 newtermios.c_lflag &= ~ECHO;
305 tcsetattr(STDIN_FILENO, TCSAFLUSH, &newtermios);
306 fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | O_NONBLOCK);
309 static void term_cleanup(void)
311 tcsetattr(STDIN_FILENO, 0, &termios);
314 static void sigcont(int sig)
316 term_setup();
319 static char *usage = "usage: fbff [options] file\n"
320 "\noptions:\n"
321 " -m x magnify the screen by repeating pixels\n"
322 " -z x zoom the screen using ffmpeg\n"
323 " -j x jump every x video frames; for slow machines\n"
324 " -f start full screen\n"
325 " -v video only playback\n"
326 " -a audio only playback\n"
327 " -t use time based seeking; only if the default does't work\n"
328 " -R adjust the video to the right of the screen\n\n";
330 static void read_args(int argc, char *argv[])
332 int i = 1;
333 while (i < argc) {
334 if (!strcmp(argv[i], "-m"))
335 magnify = atoi(argv[++i]);
336 if (!strcmp(argv[i], "-z"))
337 zoom = atof(argv[++i]);
338 if (!strcmp(argv[i], "-j"))
339 jump = atoi(argv[++i]);
340 if (!strcmp(argv[i], "-f"))
341 fullscreen = 1;
342 if (!strcmp(argv[i], "-a"))
343 video = 0;
344 if (!strcmp(argv[i], "-v"))
345 audio = 0;
346 if (!strcmp(argv[i], "-t"))
347 frame_jmp = 1024 / 32;
348 if (!strcmp(argv[i], "-h"))
349 printf(usage);
350 if (!strcmp(argv[i], "-R"))
351 just = 1;
352 i++;
356 int main(int argc, char *argv[])
358 pthread_t a_thread;
359 char *path = argv[argc - 1];
360 if (argc < 2) {
361 printf("usage: %s [options] filename\n", argv[0]);
362 return 1;
364 read_args(argc, argv);
365 ffs_globinit();
366 if (video && !(vffs = ffs_alloc(path, 1)))
367 video = 0;
368 if (audio && !(affs = ffs_alloc(path, 0)))
369 audio = 0;
370 if (!video && !audio)
371 return 1;
372 if (audio)
373 pthread_create(&a_thread, NULL, process_audio, NULL);
374 if (video) {
375 int w, h;
376 if (fb_init())
377 return 1;
378 ffs_vinfo(vffs, &w, &h);
379 if (magnify != 1 && sizeof(fbval_t) != FBM_BPP(fb_mode()))
380 fprintf(stderr, "fbff: magnify != 1 and fbval_t doesn't match\n");
381 if (fullscreen)
382 zoom = (float) fb_cols() / w / magnify;
383 ffs_vsetup(vffs, zoom, fb_mode());
385 term_setup();
386 signal(SIGCONT, sigcont);
387 mainloop();
388 term_cleanup();
390 if (video) {
391 fb_free();
392 ffs_free(vffs);
394 if (audio) {
395 pthread_join(a_thread, NULL);
396 ffs_free(affs);
398 return 0;