calculate avdiff based on audio/video packet frame ratio
[fbff.git] / fbff.c
bloba0e9d86faa151db221d8db0d4f78f7f6a1f98599
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 <libavcodec/avcodec.h>
22 #include "config.h"
23 #include "ffs.h"
24 #include "draw.h"
26 #define FF_PLAY 0
27 #define FF_PAUSE 1
28 #define FF_EXIT 2
30 static int frame_jmp = 1; /* the changes to pos_cur for each frame */
31 static int afd; /* oss fd */
33 static int arg;
34 static struct termios termios;
35 static int cmd;
37 static float zoom = 1;
38 static int magnify = 0;
39 static int jump = 0;
40 static int fullscreen = 0;
41 static int audio = 1;
42 static int video = 1;
43 static int just = 0;
45 static struct ffs *affs; /* audio ffmpeg stream */
46 static struct ffs *vffs; /* video ffmpeg stream */
48 static void draw_frame(fbval_t *img, int linelen)
50 int w, h;
51 fbval_t buf[1 << 14];
52 int nr, nc, cb;
53 int i, r, c;
54 ffs_vinfo(vffs, &w, &h);
55 nr = MIN(h * zoom, fb_rows() / magnify);
56 nc = MIN(w * zoom, fb_cols() / magnify);
57 cb = just ? fb_cols() - nc * magnify : 0;
58 for (r = 0; r < nr; r++) {
59 fbval_t *row = (void *) img + r * linelen;
60 if (magnify == 1) {
61 fb_set(r, cb, row, nc);
62 continue;
64 for (c = 0; c < nc; c++)
65 for (i = 0; i < magnify; i++)
66 buf[c * magnify + i] = row[c];
67 for (i = 0; i < magnify; i++)
68 fb_set(r * magnify + i, cb, buf, nc * magnify);
72 #define ABUFSZ (1 << 18)
73 #define BUFS (1 << 6)
74 static int a_cons;
75 static int a_prod;
76 static char a_buf[BUFS][ABUFSZ];
77 static int a_len[BUFS];
78 static int a_reset;
80 static int a_conswait(void)
82 return a_cons == a_prod;
85 static int a_prodwait(void)
87 return ((a_prod + 1) & (BUFS - 1)) == a_cons;
90 static void a_doreset(int pause)
92 a_reset = 1 + pause;
93 while (audio && a_reset)
94 usleep(1000);
97 static int readkey(void)
99 char b;
100 if (read(STDIN_FILENO, &b, 1) <= 0)
101 return -1;
102 return b;
105 static void waitkey(void)
107 struct pollfd ufds[1];
108 ufds[0].fd = STDIN_FILENO;
109 ufds[0].events = POLLIN;
110 poll(ufds, 1, -1);
113 static int ffarg(void)
115 int n = arg;
116 arg = 0;
117 return n ? n : 1;
120 static void ffjmp(int n, int rel)
122 struct ffs *ffs = video ? vffs : affs;
123 long pos = ffs_pos(ffs, n);
124 a_doreset(0);
125 if (audio)
126 ffs_seek(affs, pos, frame_jmp);
127 if (video)
128 ffs_seek(vffs, pos, frame_jmp);
131 static void printinfo(void)
133 struct ffs *ffs = video ? vffs : affs;
134 printf("fbff: %8lx\r", ffs_pos(ffs, 0));
135 fflush(stdout);
138 #define JMP1 (1 << 5)
139 #define JMP2 (JMP1 << 3)
140 #define JMP3 (JMP2 << 5)
142 static void execkey(void)
144 int c;
145 while ((c = readkey()) != -1) {
146 switch (c) {
147 case 'q':
148 cmd = FF_EXIT;
149 break;
150 case 'l':
151 ffjmp(ffarg() * JMP1, 1);
152 break;
153 case 'h':
154 ffjmp(-ffarg() * JMP1, 1);
155 break;
156 case 'j':
157 ffjmp(ffarg() * JMP2, 1);
158 break;
159 case 'k':
160 ffjmp(-ffarg() * JMP2, 1);
161 break;
162 case 'J':
163 ffjmp(ffarg() * JMP3, 1);
164 break;
165 case 'K':
166 ffjmp(-ffarg() * JMP3, 1);
167 break;
168 case '%':
169 if (arg)
170 ffjmp(100, 0);
171 break;
172 case 'i':
173 printinfo();
174 break;
175 case ' ':
176 case 'p':
177 cmd = cmd ? FF_PLAY : FF_PAUSE;
178 break;
179 case 27:
180 arg = 0;
181 break;
182 default:
183 if (isdigit(c))
184 arg = arg * 10 + c - '0';
189 static int is_vsync(void)
191 int cur = ffs_seq(affs, 0);
192 int all = ffs_seq(affs, 1);
193 int ratio = all ? (all - cur) * 1024 / all : 512;
194 int avdiff = BUFS * 4 * ratio / 1024;
195 return ffs_seq(vffs, 1) + avdiff < ffs_seq(affs, 1);
198 static void mainloop(void)
200 while (cmd != FF_EXIT) {
201 execkey();
202 if (cmd == FF_PAUSE) {
203 a_doreset(1);
204 waitkey();
205 continue;
207 while (audio && !a_prodwait()) {
208 int ret = ffs_adec(affs, a_buf[a_prod], ABUFSZ);
209 if (ret < 0)
210 goto eof;
211 if (ret > 0) {
212 a_len[a_prod] = ret;
213 a_prod = (a_prod + 1) & (BUFS - 1);
216 if (video && (!audio || 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 goto eof;
222 if (ret > 0)
223 draw_frame((void *) buf, ret);
224 ffs_wait(vffs);
227 eof:
228 cmd = FF_EXIT;
229 a_doreset(0);
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() || cmd == FF_PAUSE)) {
256 if (cmd == FF_EXIT)
257 goto ret;
258 usleep(1000);
260 if (a_reset) {
261 if (a_reset == 1)
262 a_cons = a_prod;
263 a_reset = 0;
264 continue;
266 write(afd, a_buf[a_cons], a_len[a_cons]);
267 a_cons = (a_cons + 1) & (BUFS - 1);
269 ret:
270 oss_close();
271 return NULL;
274 static void term_setup(void)
276 struct termios newtermios;
277 tcgetattr(STDIN_FILENO, &termios);
278 newtermios = termios;
279 newtermios.c_lflag &= ~ICANON;
280 newtermios.c_lflag &= ~ECHO;
281 tcsetattr(STDIN_FILENO, TCSAFLUSH, &newtermios);
282 fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | O_NONBLOCK);
285 static void term_cleanup(void)
287 tcsetattr(STDIN_FILENO, 0, &termios);
290 static void sigcont(int sig)
292 term_setup();
295 static char *usage = "usage: fbff [options] file\n"
296 "\noptions:\n"
297 " -m x magnify the screen by repeating pixels\n"
298 " -z x zoom the screen using ffmpeg\n"
299 " -j x jump every x video frames; for slow machines\n"
300 " -f start full screen\n"
301 " -v video only playback\n"
302 " -a audio only playback\n"
303 " -t use time based seeking; only if the default does't work\n"
304 " -R adjust the video to the right of the screen\n\n";
306 static void read_args(int argc, char *argv[])
308 int i = 1;
309 while (i < argc) {
310 if (!strcmp(argv[i], "-m"))
311 magnify = atoi(argv[++i]);
312 if (!strcmp(argv[i], "-z"))
313 zoom = atof(argv[++i]);
314 if (!strcmp(argv[i], "-j"))
315 jump = atoi(argv[++i]);
316 if (!strcmp(argv[i], "-f"))
317 fullscreen = 1;
318 if (!strcmp(argv[i], "-a"))
319 video = 0;
320 if (!strcmp(argv[i], "-v"))
321 audio = 0;
322 if (!strcmp(argv[i], "-t"))
323 frame_jmp = 1024 / 32;
324 if (!strcmp(argv[i], "-h"))
325 printf(usage);
326 if (!strcmp(argv[i], "-R"))
327 just = 1;
328 i++;
332 int main(int argc, char *argv[])
334 pthread_t a_thread;
335 char *path = argv[argc - 1];
336 if (argc < 2) {
337 printf("usage: %s [options] filename\n", argv[0]);
338 return 1;
340 read_args(argc, argv);
341 ffs_globinit();
342 if (video && !(vffs = ffs_alloc(path, 1)))
343 video = 0;
344 if (audio && !(affs = ffs_alloc(path, 0)))
345 audio = 0;
346 if (!video && !audio)
347 return 1;
348 if (audio)
349 pthread_create(&a_thread, NULL, process_audio, NULL);
350 if (video) {
351 int w, h;
352 fb_init();
353 ffs_vinfo(vffs, &w, &h);
354 if (!magnify)
355 magnify = fb_cols() / w / zoom;
356 if (fullscreen)
357 zoom = (float) fb_cols() / w / magnify;
358 ffs_vsetup(vffs, zoom, FFMPEG_PIXFMT);
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;