d083bbdbd37c5e734146b43f9b8bac42f41d63a3
[fbff.git] / fbff.c
blobd083bbdbd37c5e734146b43f9b8bac42f41d63a3
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 video = 1; /* video stream; 0=none, 1=auto, >2=idx */
38 static int audio = 1; /* audio stream; 0=none, 1=auto, >2=idx */
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 = 16; /* synchronization steps */
49 static int sync_cur; /* synchronization steps left */
51 static void stroll(void)
53 usleep(10000);
56 static void draw_frame(void *img, int linelen)
58 int w, h;
59 fbval_t buf[1 << 14];
60 int nr, nc, cb;
61 int i, r, c;
62 ffs_vinfo(vffs, &w, &h);
63 nr = MIN(h * zoom, fb_rows() / magnify);
64 nc = MIN(w * zoom, fb_cols() / magnify);
65 cb = just ? fb_cols() - nc * magnify : 0;
66 for (r = 0; r < nr; r++) {
67 fbval_t *row = img + r * linelen;
68 if (magnify == 1) {
69 fb_set(r, cb, row, nc);
70 continue;
72 for (c = 0; c < nc; c++)
73 for (i = 0; i < magnify; i++)
74 buf[c * magnify + i] = row[c];
75 for (i = 0; i < magnify; i++)
76 fb_set(r * magnify + i, cb, buf, nc * magnify);
80 #define ABUFSZ (1 << 18)
82 static int a_cons;
83 static int a_prod;
84 static char a_buf[AUDIOBUFS][ABUFSZ];
85 static int a_len[AUDIOBUFS];
86 static int a_reset;
88 static int a_conswait(void)
90 return a_cons == a_prod;
93 static int a_prodwait(void)
95 return ((a_prod + 1) & (AUDIOBUFS - 1)) == a_cons;
98 static void a_doreset(int pause)
100 a_reset = 1 + pause;
101 while (audio && a_reset)
102 stroll();
105 static int readkey(void)
107 char b;
108 if (read(STDIN_FILENO, &b, 1) <= 0)
109 return -1;
110 return b;
113 static void waitkey(void)
115 struct pollfd ufds[1];
116 ufds[0].fd = STDIN_FILENO;
117 ufds[0].events = POLLIN;
118 poll(ufds, 1, -1);
121 static int ffarg(int def)
123 int n = arg;
124 arg = 0;
125 return n ? n : def;
128 static void ffjmp(int n, int rel)
130 struct ffs *ffs = video ? vffs : affs;
131 long pos = ffs_pos(ffs, n);
132 a_doreset(0);
133 if (audio)
134 ffs_seek(affs, pos, frame_jmp);
135 if (video)
136 ffs_seek(vffs, pos, frame_jmp);
139 static void printinfo(void)
141 struct ffs *ffs = video ? vffs : affs;
142 printf("fbff: %8lx \t (AV: %d)\r",
143 ffs_pos(ffs, 0), video && audio ? ffs_avdiff(vffs, affs) : 0);
144 fflush(stdout);
147 #define JMP1 (1 << 5)
148 #define JMP2 (JMP1 << 3)
149 #define JMP3 (JMP2 << 5)
151 static void execkey(void)
153 int c;
154 while ((c = readkey()) != -1) {
155 switch (c) {
156 case 'q':
157 exited = 1;
158 break;
159 case 'l':
160 ffjmp(ffarg(1) * JMP1, 1);
161 break;
162 case 'h':
163 ffjmp(-ffarg(1) * JMP1, 1);
164 break;
165 case 'j':
166 ffjmp(ffarg(1) * JMP2, 1);
167 break;
168 case 'k':
169 ffjmp(-ffarg(1) * JMP2, 1);
170 break;
171 case 'J':
172 ffjmp(ffarg(1) * JMP3, 1);
173 break;
174 case 'K':
175 ffjmp(-ffarg(1) * JMP3, 1);
176 break;
177 case '%':
178 if (arg)
179 ffjmp(100, 0);
180 break;
181 case 'i':
182 printinfo();
183 break;
184 case ' ':
185 case 'p':
186 paused = !paused;
187 sync_cur = sync_cnt;
188 break;
189 case '-':
190 sync_diff = -ffarg(0);
191 break;
192 case '+':
193 sync_diff = ffarg(0);
194 break;
195 case 'a':
196 sync_diff = ffs_avdiff(vffs, affs);
197 break;
198 case 'c':
199 sync_cnt = ffarg(0);
200 break;
201 case 's':
202 sync_cur = ffarg(sync_cnt);
203 break;
204 case 27:
205 arg = 0;
206 break;
207 default:
208 if (isdigit(c))
209 arg = arg * 10 + c - '0';
214 /* return nonzero if one more video frame can be decoded */
215 static int vsync(void)
217 if (sync_cur > 0) {
218 sync_cur--;
219 return ffs_avdiff(vffs, affs) >= sync_diff;
221 ffs_wait(vffs);
222 return 1;
225 static void mainloop(void)
227 int eof = 0;
228 while (eof < audio + video) {
229 execkey();
230 if (exited)
231 break;
232 if (paused) {
233 a_doreset(1);
234 waitkey();
235 continue;
237 while (audio && !eof && !a_prodwait()) {
238 int ret = ffs_adec(affs, a_buf[a_prod], ABUFSZ);
239 if (ret < 0)
240 eof++;
241 if (ret > 0) {
242 a_len[a_prod] = ret;
243 a_prod = (a_prod + 1) & (AUDIOBUFS - 1);
246 if (video && (!audio || eof || vsync())) {
247 int ignore = jump && (vnum++ % (jump + 1));
248 void *buf;
249 int ret = ffs_vdec(vffs, ignore ? NULL : &buf);
250 if (ret < 0)
251 eof++;
252 if (ret > 0)
253 draw_frame((void *) buf, ret);
254 } else {
255 stroll();
258 exited = 1;
261 static void oss_init(void)
263 int rate, ch, bps;
264 afd = open("/dev/dsp", O_RDWR);
265 if (afd < 0) {
266 fprintf(stderr, "cannot open /dev/dsp\n");
267 exit(1);
269 ffs_ainfo(affs, &rate, &bps, &ch);
270 ioctl(afd, SOUND_PCM_WRITE_RATE, &rate);
271 ioctl(afd, SOUND_PCM_WRITE_CHANNELS, &ch);
272 ioctl(afd, SOUND_PCM_WRITE_BITS, &bps);
275 static void oss_close(void)
277 close(afd);
280 static void *process_audio(void *dat)
282 oss_init();
283 while (1) {
284 while (!a_reset && (a_conswait() || paused) && !exited)
285 stroll();
286 if (exited)
287 goto ret;
288 if (a_reset) {
289 if (a_reset == 1)
290 a_cons = a_prod;
291 a_reset = 0;
292 continue;
294 write(afd, a_buf[a_cons], a_len[a_cons]);
295 a_cons = (a_cons + 1) & (AUDIOBUFS - 1);
297 ret:
298 oss_close();
299 return NULL;
302 static void term_setup(void)
304 struct termios newtermios;
305 tcgetattr(STDIN_FILENO, &termios);
306 newtermios = termios;
307 newtermios.c_lflag &= ~ICANON;
308 newtermios.c_lflag &= ~ECHO;
309 tcsetattr(STDIN_FILENO, TCSAFLUSH, &newtermios);
310 fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | O_NONBLOCK);
313 static void term_cleanup(void)
315 tcsetattr(STDIN_FILENO, 0, &termios);
318 static void sigcont(int sig)
320 term_setup();
323 static char *usage = "usage: fbff [options] file\n"
324 "\noptions:\n"
325 " -z x zoom the screen using ffmpeg\n"
326 " -m x magnify the screen by repeating pixels\n"
327 " -j x jump every x video frames; for slow machines\n"
328 " -f start full screen\n"
329 " -v x select video stream; '-' disables video\n"
330 " -a x select audio stream; '-' disables audio\n"
331 " -s always synchronize; useful for files with bad video framerate\n"
332 " -t use time based seeking; only if the default does't work\n"
333 " -R adjust the video to the right of the screen\n\n";
335 static void read_args(int argc, char *argv[])
337 int i = 1;
338 while (i < argc) {
339 if (!strcmp(argv[i], "-m"))
340 magnify = atoi(argv[++i]);
341 if (!strcmp(argv[i], "-z"))
342 zoom = atof(argv[++i]);
343 if (!strcmp(argv[i], "-j"))
344 jump = atoi(argv[++i]);
345 if (!strcmp(argv[i], "-f"))
346 fullscreen = 1;
347 if (!strcmp(argv[i], "-s"))
348 sync_cnt = sync_cur = (1 << 30);
349 if (!strcmp(argv[i], "-v"))
350 video = argv[++i][0] == '-' ? 0 : atoi(argv[i]) + 2;
351 if (!strcmp(argv[i], "-a"))
352 audio = argv[++i][0] == '-' ? 0 : atoi(argv[i]) + 2;
353 if (!strcmp(argv[i], "-t"))
354 frame_jmp = 1024;
355 if (!strcmp(argv[i], "-h"))
356 printf(usage);
357 if (!strcmp(argv[i], "-R"))
358 just = 1;
359 i++;
363 int main(int argc, char *argv[])
365 pthread_t a_thread;
366 char *path = argv[argc - 1];
367 if (argc < 2) {
368 printf("usage: %s [options] filename\n", argv[0]);
369 return 1;
371 read_args(argc, argv);
372 ffs_globinit();
373 if (video && !(vffs = ffs_alloc(path, FFS_VIDEO | video - 1)))
374 video = 0;
375 if (audio && !(affs = ffs_alloc(path, FFS_AUDIO | audio - 1)))
376 audio = 0;
377 if (!video && !audio)
378 return 1;
379 if (audio)
380 pthread_create(&a_thread, NULL, process_audio, NULL);
381 if (video) {
382 int w, h;
383 if (fb_init())
384 return 1;
385 ffs_vinfo(vffs, &w, &h);
386 if (magnify != 1 && sizeof(fbval_t) != FBM_BPP(fb_mode()))
387 fprintf(stderr, "fbff: magnify != 1 and fbval_t doesn't match\n");
388 if (fullscreen)
389 zoom = (float) fb_cols() / w / magnify;
390 ffs_vsetup(vffs, zoom, fb_mode());
392 term_setup();
393 signal(SIGCONT, sigcont);
394 mainloop();
395 term_cleanup();
397 if (video) {
398 fb_free();
399 ffs_free(vffs);
401 if (audio) {
402 pthread_join(a_thread, NULL);
403 ffs_free(affs);
405 return 0;