draw: draw.c is now independent of framebuffer depth
[fbff.git] / fbff.c
blob321e14fd7a40338a1e747fc86e6af35690565e1d
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 #define FF_PLAY 0
29 #define FF_PAUSE 1
30 #define FF_EXIT 2
32 static int frame_jmp = 1; /* the changes to pos_cur for each frame */
33 static int afd; /* oss fd */
35 static int arg;
36 static struct termios termios;
37 static int cmd;
39 static float zoom = 1;
40 static int magnify = 0;
41 static int jump = 0;
42 static int fullscreen = 0;
43 static int audio = 1;
44 static int video = 1;
45 static int just = 0;
47 static struct ffs *affs; /* audio ffmpeg stream */
48 static struct ffs *vffs; /* video ffmpeg stream */
50 static void draw_frame(void *img, int linelen)
52 int w, h;
53 fbval_t buf[1 << 14];
54 int nr, nc, cb;
55 int i, r, c;
56 ffs_vinfo(vffs, &w, &h);
57 nr = MIN(h * zoom, fb_rows() / magnify);
58 nc = MIN(w * zoom, fb_cols() / magnify);
59 cb = just ? fb_cols() - nc * magnify : 0;
60 for (r = 0; r < nr; r++) {
61 fbval_t *row = img + r * linelen;
62 if (magnify == 1) {
63 fb_set(r, cb, row, nc);
64 continue;
66 for (c = 0; c < nc; c++)
67 for (i = 0; i < magnify; i++)
68 buf[c * magnify + i] = row[c];
69 for (i = 0; i < magnify; i++)
70 fb_set(r * magnify + i, cb, buf, nc * magnify);
74 #define ABUFSZ (1 << 18)
76 static int a_cons;
77 static int a_prod;
78 static char a_buf[AUDIOBUFS][ABUFSZ];
79 static int a_len[AUDIOBUFS];
80 static int a_reset;
82 static int a_conswait(void)
84 return a_cons == a_prod;
87 static int a_prodwait(void)
89 return ((a_prod + 1) & (AUDIOBUFS - 1)) == a_cons;
92 static void a_doreset(int pause)
94 a_reset = 1 + pause;
95 while (audio && a_reset)
96 usleep(1000);
99 static int readkey(void)
101 char b;
102 if (read(STDIN_FILENO, &b, 1) <= 0)
103 return -1;
104 return b;
107 static void waitkey(void)
109 struct pollfd ufds[1];
110 ufds[0].fd = STDIN_FILENO;
111 ufds[0].events = POLLIN;
112 poll(ufds, 1, -1);
115 static int ffarg(void)
117 int n = arg;
118 arg = 0;
119 return n ? n : 1;
122 static void ffjmp(int n, int rel)
124 struct ffs *ffs = video ? vffs : affs;
125 long pos = ffs_pos(ffs, n);
126 a_doreset(0);
127 if (audio)
128 ffs_seek(affs, pos, frame_jmp);
129 if (video)
130 ffs_seek(vffs, pos, frame_jmp);
133 static void printinfo(void)
135 struct ffs *ffs = video ? vffs : affs;
136 printf("fbff: %8lx\r", ffs_pos(ffs, 0));
137 fflush(stdout);
140 #define JMP1 (1 << 5)
141 #define JMP2 (JMP1 << 3)
142 #define JMP3 (JMP2 << 5)
144 static void execkey(void)
146 int c;
147 while ((c = readkey()) != -1) {
148 switch (c) {
149 case 'q':
150 cmd = FF_EXIT;
151 break;
152 case 'l':
153 ffjmp(ffarg() * JMP1, 1);
154 break;
155 case 'h':
156 ffjmp(-ffarg() * JMP1, 1);
157 break;
158 case 'j':
159 ffjmp(ffarg() * JMP2, 1);
160 break;
161 case 'k':
162 ffjmp(-ffarg() * JMP2, 1);
163 break;
164 case 'J':
165 ffjmp(ffarg() * JMP3, 1);
166 break;
167 case 'K':
168 ffjmp(-ffarg() * JMP3, 1);
169 break;
170 case '%':
171 if (arg)
172 ffjmp(100, 0);
173 break;
174 case 'i':
175 printinfo();
176 break;
177 case ' ':
178 case 'p':
179 cmd = cmd ? FF_PLAY : FF_PAUSE;
180 break;
181 case 27:
182 arg = 0;
183 break;
184 default:
185 if (isdigit(c))
186 arg = arg * 10 + c - '0';
191 static int is_vsync(void)
193 int cur = ffs_seq(affs, 0);
194 int all = ffs_seq(affs, 1);
195 int ratio = all ? (all - cur) * 1024 / all : 512;
196 int avdiff = AUDIOBUFS * 4 * ratio / 1024;
197 return ffs_seq(vffs, 1) + avdiff < ffs_seq(affs, 1);
200 static void mainloop(void)
202 int eof = 0;
203 while (cmd != FF_EXIT) {
204 execkey();
205 if (cmd == FF_PAUSE) {
206 a_doreset(1);
207 waitkey();
208 continue;
210 while (audio && !eof && !a_prodwait()) {
211 int ret = ffs_adec(affs, a_buf[a_prod], ABUFSZ);
212 if (ret < 0)
213 eof = 1;
214 if (ret > 0) {
215 a_len[a_prod] = ret;
216 a_prod = (a_prod + 1) & (AUDIOBUFS - 1);
219 if (video && (!audio || eof || is_vsync())) {
220 int ignore = jump && !(ffs_seq(vffs, 0) % (jump + 1));
221 char *buf;
222 int ret = ffs_vdec(vffs, ignore ? NULL : &buf);
223 if (ret < 0)
224 break;
225 if (ret > 0)
226 draw_frame((void *) buf, ret);
227 ffs_wait(vffs);
230 cmd = FF_EXIT;
231 a_doreset(0);
234 static void oss_init(void)
236 int rate, ch, bps;
237 afd = open("/dev/dsp", O_RDWR);
238 if (afd < 0) {
239 fprintf(stderr, "cannot open /dev/dsp\n");
240 exit(1);
242 ffs_ainfo(affs, &rate, &bps, &ch);
243 ioctl(afd, SOUND_PCM_WRITE_RATE, &rate);
244 ioctl(afd, SOUND_PCM_WRITE_CHANNELS, &ch);
245 ioctl(afd, SOUND_PCM_WRITE_BITS, &bps);
248 static void oss_close(void)
250 close(afd);
253 static void *process_audio(void *dat)
255 oss_init();
256 while (1) {
257 while (!a_reset && (a_conswait() || cmd == FF_PAUSE)) {
258 if (cmd == FF_EXIT)
259 goto ret;
260 usleep(1000);
262 if (a_reset) {
263 if (a_reset == 1)
264 a_cons = a_prod;
265 a_reset = 0;
266 continue;
268 write(afd, a_buf[a_cons], a_len[a_cons]);
269 a_cons = (a_cons + 1) & (AUDIOBUFS - 1);
271 ret:
272 oss_close();
273 return NULL;
276 static void term_setup(void)
278 struct termios newtermios;
279 tcgetattr(STDIN_FILENO, &termios);
280 newtermios = termios;
281 newtermios.c_lflag &= ~ICANON;
282 newtermios.c_lflag &= ~ECHO;
283 tcsetattr(STDIN_FILENO, TCSAFLUSH, &newtermios);
284 fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | O_NONBLOCK);
287 static void term_cleanup(void)
289 tcsetattr(STDIN_FILENO, 0, &termios);
292 static void sigcont(int sig)
294 term_setup();
297 static char *usage = "usage: fbff [options] file\n"
298 "\noptions:\n"
299 " -m x magnify the screen by repeating pixels\n"
300 " -z x zoom the screen using ffmpeg\n"
301 " -j x jump every x video frames; for slow machines\n"
302 " -f start full screen\n"
303 " -v video only playback\n"
304 " -a audio only playback\n"
305 " -t use time based seeking; only if the default does't work\n"
306 " -R adjust the video to the right of the screen\n\n";
308 static void read_args(int argc, char *argv[])
310 int i = 1;
311 while (i < argc) {
312 if (!strcmp(argv[i], "-m"))
313 magnify = atoi(argv[++i]);
314 if (!strcmp(argv[i], "-z"))
315 zoom = atof(argv[++i]);
316 if (!strcmp(argv[i], "-j"))
317 jump = atoi(argv[++i]);
318 if (!strcmp(argv[i], "-f"))
319 fullscreen = 1;
320 if (!strcmp(argv[i], "-a"))
321 video = 0;
322 if (!strcmp(argv[i], "-v"))
323 audio = 0;
324 if (!strcmp(argv[i], "-t"))
325 frame_jmp = 1024 / 32;
326 if (!strcmp(argv[i], "-h"))
327 printf(usage);
328 if (!strcmp(argv[i], "-R"))
329 just = 1;
330 i++;
334 int main(int argc, char *argv[])
336 pthread_t a_thread;
337 char *path = argv[argc - 1];
338 if (argc < 2) {
339 printf("usage: %s [options] filename\n", argv[0]);
340 return 1;
342 read_args(argc, argv);
343 ffs_globinit();
344 if (video && !(vffs = ffs_alloc(path, 1)))
345 video = 0;
346 if (audio && !(affs = ffs_alloc(path, 0)))
347 audio = 0;
348 if (!video && !audio)
349 return 1;
350 if (audio)
351 pthread_create(&a_thread, NULL, process_audio, NULL);
352 if (video) {
353 int w, h;
354 if (fb_init())
355 return 1;
356 ffs_vinfo(vffs, &w, &h);
357 if (!magnify)
358 magnify = MAX(1, fb_cols() / w / zoom);
359 if (fullscreen)
360 zoom = (float) fb_cols() / w / magnify;
361 ffs_vsetup(vffs, zoom, fb_mode());
363 term_setup();
364 signal(SIGCONT, sigcont);
365 mainloop();
366 term_cleanup();
368 if (video) {
369 fb_free();
370 ffs_free(vffs);
372 if (audio) {
373 pthread_join(a_thread, NULL);
374 ffs_free(affs);
376 return 0;