port of fbff to libmpeg3
[fbff.git] / fbff.c
bloba726c799a27435af6e6edb463b71460ceff837f1
1 /*
2 * fbff - a small ffmpeg-based framebuffer/oss media player
4 * Copyright (C) 2009-2013 Ali Gholami Rudi
6 * This program is released under the modified BSD license.
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 rjust = 0; /* justify video to the right */
40 static int bjust = 0; /* justify video to the bottom */
41 static int frame_jmp = 1; /* the changes to pos_cur for each frame */
43 static struct ffs *affs; /* audio ffmpeg stream */
44 static struct ffs *vffs; /* video ffmpeg stream */
45 static int afd; /* oss fd */
46 static int vnum; /* decoded video frame count */
48 static int sync_diff; /* audio/video frame position diff */
49 static int sync_period; /* sync after every this many number of frames */
50 static int sync_since; /* frames since th last sync */
51 static int sync_cnt = 32; /* synchronization steps */
52 static int sync_cur; /* synchronization steps left */
53 static int sync_first; /* first frame to record sync_diff */
55 static void stroll(void)
57 usleep(10000);
60 static void draw_frame(void *img, int linelen)
62 int w, h;
63 fbval_t buf[1 << 14];
64 int nr, nc, cb, rb;
65 int i, r, c;
66 ffs_vinfo(vffs, &w, &h);
67 nr = MIN(h * zoom, fb_rows() / magnify);
68 nc = MIN(w * zoom, fb_cols() / magnify);
69 cb = rjust ? fb_cols() - nc * magnify : 0;
70 rb = bjust ? fb_rows() - nr * magnify : 0;
71 for (r = 0; r < nr; r++) {
72 fbval_t *row = img + r * linelen;
73 if (magnify == 1) {
74 fb_set(rb + r, cb, row, nc);
75 continue;
77 for (c = 0; c < nc; c++)
78 for (i = 0; i < magnify; i++)
79 buf[c * magnify + i] = row[c];
80 for (i = 0; i < magnify; i++)
81 fb_set((rb + r) * magnify + i, cb, buf, nc * magnify);
85 #define ABUFSZ (1 << 18)
87 static int a_cons;
88 static int a_prod;
89 static char a_buf[AUDIOBUFS][ABUFSZ];
90 static int a_len[AUDIOBUFS];
91 static int a_reset;
93 static int a_conswait(void)
95 return a_cons == a_prod;
98 static int a_prodwait(void)
100 return ((a_prod + 1) & (AUDIOBUFS - 1)) == a_cons;
103 static void a_doreset(int pause)
105 a_reset = 1 + pause;
106 while (audio && a_reset)
107 stroll();
110 static int readkey(void)
112 char b;
113 if (read(STDIN_FILENO, &b, 1) <= 0)
114 return -1;
115 return b;
118 static void waitkey(void)
120 struct pollfd ufds[1];
121 ufds[0].fd = STDIN_FILENO;
122 ufds[0].events = POLLIN;
123 poll(ufds, 1, -1);
126 static int ffarg(int def)
128 int n = arg;
129 arg = 0;
130 return n ? n : def;
133 static void ffjmp(int n, int rel)
135 struct ffs *ffs = video ? vffs : affs;
136 long pos = ffs_pos(ffs, n);
137 a_doreset(0);
138 sync_cur = sync_cnt;
139 if (audio)
140 ffs_seek(affs, pos, frame_jmp);
141 if (video)
142 ffs_seek(vffs, pos, frame_jmp);
145 static void printinfo(void)
147 struct ffs *ffs = video ? vffs : affs;
148 printf("fbff: %8lx \t (AV: %d)\r",
149 ffs_pos(ffs, 0), video && audio ? ffs_avdiff(vffs, affs) : 0);
150 fflush(stdout);
153 #define JMP1 (1 << 5)
154 #define JMP2 (JMP1 << 3)
155 #define JMP3 (JMP2 << 5)
157 static void execkey(void)
159 int c;
160 while ((c = readkey()) != -1) {
161 switch (c) {
162 case 'q':
163 exited = 1;
164 break;
165 case 'l':
166 ffjmp(ffarg(1) * JMP1, 1);
167 break;
168 case 'h':
169 ffjmp(-ffarg(1) * JMP1, 1);
170 break;
171 case 'j':
172 ffjmp(ffarg(1) * JMP2, 1);
173 break;
174 case 'k':
175 ffjmp(-ffarg(1) * JMP2, 1);
176 break;
177 case 'J':
178 ffjmp(ffarg(1) * JMP3, 1);
179 break;
180 case 'K':
181 ffjmp(-ffarg(1) * JMP3, 1);
182 break;
183 case '%':
184 if (arg)
185 ffjmp(100, 0);
186 break;
187 case 'i':
188 printinfo();
189 break;
190 case ' ':
191 case 'p':
192 paused = !paused;
193 sync_cur = sync_cnt;
194 break;
195 case '-':
196 sync_diff = -ffarg(0);
197 break;
198 case '+':
199 sync_diff = ffarg(0);
200 break;
201 case 'a':
202 sync_diff = ffs_avdiff(vffs, affs);
203 break;
204 case 'c':
205 sync_cnt = ffarg(0);
206 break;
207 case 's':
208 sync_cur = ffarg(sync_cnt);
209 break;
210 case 27:
211 arg = 0;
212 break;
213 default:
214 if (isdigit(c))
215 arg = arg * 10 + c - '0';
220 /* return nonzero if one more video frame can be decoded */
221 static int vsync(void)
223 if (sync_period && sync_since++ >= sync_period) {
224 sync_cur = sync_cnt;
225 sync_since = 0;
227 if (sync_first) {
228 sync_cur = 0;
229 if (sync_first < vnum) {
230 sync_first = 0;
231 sync_diff = ffs_avdiff(vffs, affs);
234 if (sync_cur > 0) {
235 sync_cur--;
236 return ffs_avdiff(vffs, affs) >= sync_diff;
238 ffs_wait(vffs);
239 return 1;
242 static void mainloop(void)
244 int eof = 0;
245 while (eof < audio + video) {
246 execkey();
247 if (exited)
248 break;
249 if (paused) {
250 a_doreset(1);
251 waitkey();
252 continue;
254 while (audio && !eof && !a_prodwait()) {
255 int ret = ffs_adec(affs, a_buf[a_prod], ABUFSZ);
256 if (ret < 0)
257 eof++;
258 if (ret > 0) {
259 a_len[a_prod] = ret;
260 a_prod = (a_prod + 1) & (AUDIOBUFS - 1);
263 if (video && (!audio || eof || vsync())) {
264 int ignore = jump && (vnum % (jump + 1));
265 void *buf;
266 int ret = ffs_vdec(vffs, ignore ? NULL : &buf);
267 vnum++;
268 if (ret < 0)
269 eof++;
270 if (ret > 0)
271 draw_frame((void *) buf, ret);
272 } else {
273 stroll();
276 exited = 1;
279 static void oss_init(void)
281 int rate, ch, bps;
282 afd = open("/dev/dsp", O_RDWR);
283 if (afd < 0) {
284 fprintf(stderr, "cannot open /dev/dsp\n");
285 exit(1);
287 ffs_ainfo(affs, &rate, &bps, &ch);
288 ioctl(afd, SOUND_PCM_WRITE_RATE, &rate);
289 ioctl(afd, SOUND_PCM_WRITE_CHANNELS, &ch);
290 ioctl(afd, SOUND_PCM_WRITE_BITS, &bps);
293 static void oss_close(void)
295 close(afd);
298 static void *process_audio(void *dat)
300 while (1) {
301 while (!a_reset && (a_conswait() || paused) && !exited)
302 stroll();
303 if (exited)
304 goto ret;
305 if (a_reset) {
306 if (a_reset == 1)
307 a_cons = a_prod;
308 a_reset = 0;
309 continue;
311 write(afd, a_buf[a_cons], a_len[a_cons]);
312 a_cons = (a_cons + 1) & (AUDIOBUFS - 1);
314 ret:
315 oss_close();
316 return NULL;
319 static void term_setup(void)
321 struct termios newtermios;
322 tcgetattr(STDIN_FILENO, &termios);
323 newtermios = termios;
324 newtermios.c_lflag &= ~ICANON;
325 newtermios.c_lflag &= ~ECHO;
326 tcsetattr(STDIN_FILENO, TCSAFLUSH, &newtermios);
327 fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | O_NONBLOCK);
330 static void term_cleanup(void)
332 tcsetattr(STDIN_FILENO, 0, &termios);
335 static void sigcont(int sig)
337 term_setup();
340 static char *usage = "usage: fbff [options] file\n"
341 "\noptions:\n"
342 " -z x zoom the screen using ffmpeg\n"
343 " -m x magnify the screen by repeating pixels\n"
344 " -j x jump every x video frames; for slow machines\n"
345 " -f start full screen\n"
346 " -v x select video stream; '-' disables video\n"
347 " -a x select audio stream; '-' disables audio\n"
348 " -s always synchronize; useful for files with bad video framerate\n"
349 " -u record avdiff after a few frames\n"
350 " -t use time based seeking; only if the default does't work\n"
351 " -r adjust the video to the right of the screen\n"
352 " -b adjust the video to the bottom of the screen\n\n";
354 static void read_args(int argc, char *argv[])
356 int i = 1;
357 while (i < argc) {
358 char *c = argv[i];
359 if (c[0] != '-')
360 break;
361 if (c[1] == 'm')
362 magnify = c[2] ? atoi(c + 2) : atoi(argv[++i]);
363 if (c[1] == 'z')
364 zoom = c[2] ? atof(c + 2) : atof(argv[++i]);
365 if (c[1] == 'j')
366 jump = c[2] ? atoi(c + 2) : atoi(argv[++i]);
367 if (c[1] == 'f')
368 fullscreen = 1;
369 if (c[1] == 's')
370 sync_period = c[2] ? atoi(c + 2) : 1;
371 if (c[1] == 't')
372 frame_jmp = 1024;
373 if (c[1] == 'h')
374 printf(usage);
375 if (c[1] == 'r')
376 rjust = 1;
377 if (c[1] == 'b')
378 bjust = 1;
379 if (c[1] == 'u')
380 sync_first = 32;
381 if (c[1] == 'v') {
382 char *arg = c[2] ? c + 2 : argv[++i];
383 video = arg[0] == '-' ? 0 : atoi(arg) + 2;
385 if (c[1] == 'a') {
386 char *arg = c[2] ? c + 2 : argv[++i];
387 audio = arg[0] == '-' ? 0 : atoi(arg) + 2;
389 i++;
393 int main(int argc, char *argv[])
395 pthread_t a_thread;
396 char *path = argv[argc - 1];
397 if (argc < 2) {
398 printf("usage: %s [options] filename\n", argv[0]);
399 return 1;
401 read_args(argc, argv);
402 ffs_globinit();
403 if (video && !(vffs = ffs_alloc(path, FFS_VIDEO | (video - 1))))
404 video = 0;
405 if (audio && !(affs = ffs_alloc(path, FFS_AUDIO | (audio - 1))))
406 audio = 0;
407 if (!video && !audio)
408 return 1;
409 if (audio) {
410 ffs_aconf(affs);
411 oss_init();
412 pthread_create(&a_thread, NULL, process_audio, NULL);
414 if (video) {
415 int w, h;
416 if (fb_init())
417 return 1;
418 ffs_vinfo(vffs, &w, &h);
419 if (magnify != 1 && sizeof(fbval_t) != FBM_BPP(fb_mode()))
420 fprintf(stderr, "fbff: magnify != 1 and fbval_t doesn't match\n");
421 if (fullscreen) {
422 float hz = (float) fb_rows() / h / magnify;
423 float wz = (float) fb_cols() / w / magnify;
424 zoom = hz < wz ? hz : wz;
426 ffs_vconf(vffs, zoom, fb_mode());
428 term_setup();
429 signal(SIGCONT, sigcont);
430 mainloop();
431 term_cleanup();
433 if (video) {
434 fb_free();
435 ffs_free(vffs);
437 if (audio) {
438 pthread_join(a_thread, NULL);
439 ffs_free(affs);
441 return 0;