2 * fbff - a small ffmpeg-based framebuffer/oss media player
4 * Copyright (C) 2009-2015 Ali Gholami Rudi
6 * This program is released under the Modified BSD license.
19 #include <sys/soundcard.h>
25 #define MIN(a, b) ((a) < (b) ? (a) : (b))
26 #define MAX(a, b) ((a) > (b) ? (a) : (b))
33 static char filename
[32];
34 static struct termios termios
;
36 static float zoom
= 1;
37 static int magnify
= 1;
39 static int fullscreen
= 0;
40 static int video
= 1; /* video stream; 0=none, 1=auto, >2=idx */
41 static int audio
= 1; /* audio stream; 0=none, 1=auto, >2=idx */
42 static int rjust
= 0; /* justify video to the right */
43 static int bjust
= 0; /* justify video to the bottom */
45 static struct ffs
*affs
; /* audio ffmpeg stream */
46 static struct ffs
*vffs
; /* video ffmpeg stream */
47 static int afd
; /* oss fd */
48 static int vnum
; /* decoded video frame count */
49 static long mark
[256]; /* marks */
51 static int sync_diff
; /* audio/video frame position diff */
52 static int sync_period
; /* sync after every this many number of frames */
53 static int sync_since
; /* frames since th last sync */
54 static int sync_cnt
= 32; /* synchronization steps */
55 static int sync_cur
; /* synchronization steps left */
56 static int sync_first
; /* first frame to record sync_diff */
58 static void stroll(void)
63 static void draw_frame(void *img
, int linelen
)
69 ffs_vinfo(vffs
, &w
, &h
);
70 nr
= MIN(h
* zoom
, fb_rows() / magnify
);
71 nc
= MIN(w
* zoom
, fb_cols() / magnify
);
72 cb
= rjust
? fb_cols() - nc
* magnify
: 0;
73 rb
= bjust
? fb_rows() - nr
* magnify
: 0;
74 for (r
= 0; r
< nr
; r
++) {
75 fbval_t
*row
= img
+ r
* linelen
;
77 fb_set(rb
+ r
, cb
, row
, nc
);
80 for (c
= 0; c
< nc
; c
++)
81 for (i
= 0; i
< magnify
; i
++)
82 buf
[c
* magnify
+ i
] = row
[c
];
83 for (i
= 0; i
< magnify
; i
++)
84 fb_set((rb
+ r
) * magnify
+ i
, cb
, buf
, nc
* magnify
);
88 static int oss_open(void)
91 afd
= open("/dev/dsp", O_WRONLY
);
94 ffs_ainfo(affs
, &rate
, &bps
, &ch
);
95 ioctl(afd
, SOUND_PCM_WRITE_CHANNELS
, &ch
);
96 ioctl(afd
, SOUND_PCM_WRITE_BITS
, &bps
);
97 ioctl(afd
, SOUND_PCM_WRITE_RATE
, &rate
);
101 static void oss_close(void)
108 #define ABUFSZ (1 << 18)
112 static char a_buf
[AUDIOBUFS
][ABUFSZ
];
113 static int a_len
[AUDIOBUFS
];
116 static int a_conswait(void)
118 return a_cons
== a_prod
;
121 static int a_prodwait(void)
123 return ((a_prod
+ 1) & (AUDIOBUFS
- 1)) == a_cons
;
126 static void a_doreset(int pause
)
129 while (audio
&& a_reset
)
133 static int cmdread(void)
136 if (read(0, &b
, 1) <= 0)
141 static void cmdwait(void)
143 struct pollfd ufds
[1];
145 ufds
[0].events
= POLLIN
;
149 static void cmdjmp(int n
, int rel
)
151 struct ffs
*ffs
= video
? vffs
: affs
;
152 long pos
= (rel
? ffs_pos(ffs
) : 0) + n
* 1000;
158 mark
['\''] = ffs_pos(ffs
);
160 ffs_seek(affs
, ffs
, pos
);
162 ffs_seek(vffs
, ffs
, pos
);
165 static void cmdinfo(void)
167 struct ffs
*ffs
= video
? vffs
: affs
;
168 long pos
= ffs_pos(ffs
);
169 long percent
= ffs_duration(ffs
) ? pos
* 1000 / ffs_duration(ffs
) : 0;
170 printf("%c %3ld.%01ld%% %3ld:%02ld.%01ld (AV:%4d) [%s] \r",
171 paused
? (afd
< 0 ? '*' : ' ') : '>',
172 percent
/ 10, percent
% 10,
173 pos
/ 60000, (pos
% 60000) / 1000, (pos
% 1000) / 100,
174 video
&& audio
? ffs_avdiff(vffs
, affs
) : 0,
179 static int cmdarg(int def
)
186 static void cmdexec(void)
189 while ((c
= cmdread()) >= 0) {
192 mark
[c
] = ffs_pos(video
? vffs
: affs
);
198 cmdjmp(mark
[c
] / 1000, 0);
206 cmdjmp(cmdarg(1) * 10, 1);
209 cmdjmp(-cmdarg(1) * 10, 1);
212 cmdjmp(cmdarg(1) * 60, 1);
215 cmdjmp(-cmdarg(1) * 60, 1);
218 cmdjmp(cmdarg(1) * 600, 1);
221 cmdjmp(-cmdarg(1) * 600, 1);
224 cmdjmp(cmdarg(0) * 60, 0);
227 cmdjmp(cmdarg(0) * ffs_duration(vffs
? vffs
: affs
) / 100000, 0);
243 if (audio
&& !paused
)
249 sync_diff
= -cmdarg(0);
252 sync_diff
= cmdarg(0);
255 sync_diff
= ffs_avdiff(vffs
, affs
);
258 sync_cnt
= cmdarg(0);
261 sync_cur
= cmdarg(sync_cnt
);
268 arg
= arg
* 10 + c
- '0';
273 /* return nonzero if one more video frame can be decoded */
274 static int vsync(void)
276 if (sync_period
&& sync_since
++ >= sync_period
) {
282 if (sync_first
< vnum
) {
284 sync_diff
= ffs_avdiff(vffs
, affs
);
289 return ffs_avdiff(vffs
, affs
) >= sync_diff
;
295 static void mainloop(void)
298 while (eof
< audio
+ video
) {
307 while (audio
&& !eof
&& !a_prodwait()) {
308 int ret
= ffs_adec(affs
, a_buf
[a_prod
], ABUFSZ
);
313 a_prod
= (a_prod
+ 1) & (AUDIOBUFS
- 1);
316 if (video
&& (!audio
|| eof
|| vsync())) {
317 int ignore
= jump
&& (vnum
% (jump
+ 1));
319 int ret
= ffs_vdec(vffs
, ignore
? NULL
: &buf
);
324 draw_frame((void *) buf
, ret
);
332 static void *process_audio(void *dat
)
335 while (!a_reset
&& (a_conswait() || paused
) && !exited
)
346 write(afd
, a_buf
[a_cons
], a_len
[a_cons
]);
347 a_cons
= (a_cons
+ 1) & (AUDIOBUFS
- 1);
353 static void term_setup(void)
355 struct termios newtermios
;
356 tcgetattr(0, &termios
);
357 newtermios
= termios
;
358 newtermios
.c_lflag
&= ~ICANON
;
359 newtermios
.c_lflag
&= ~ECHO
;
360 tcsetattr(0, TCSAFLUSH
, &newtermios
);
361 fcntl(0, F_SETFL
, fcntl(0, F_GETFL
) | O_NONBLOCK
);
364 static void term_cleanup(void)
366 tcsetattr(0, 0, &termios
);
369 static void sigcont(int sig
)
374 static char *usage
= "usage: fbff [options] file\n"
376 " -z x zoom the video\n"
377 " -m x magnify the video by duplicating pixels\n"
378 " -j x jump every x video frames; for slow machines\n"
379 " -f start full screen\n"
380 " -v x select video stream; '-' disables video\n"
381 " -a x select audio stream; '-' disables audio\n"
382 " -s always synchronize (-sx for every x frames)\n"
383 " -u record A/V delay after the first few frames\n"
384 " -r adjust the video to the right of the screen\n"
385 " -b adjust the video to the bottom of the screen\n\n";
387 static void read_args(int argc
, char *argv
[])
395 magnify
= c
[2] ? atoi(c
+ 2) : atoi(argv
[++i
]);
397 zoom
= c
[2] ? atof(c
+ 2) : atof(argv
[++i
]);
399 jump
= c
[2] ? atoi(c
+ 2) : atoi(argv
[++i
]);
403 sync_period
= c
[2] ? atoi(c
+ 2) : 1;
413 char *arg
= c
[2] ? c
+ 2 : argv
[++i
];
414 video
= arg
[0] == '-' ? 0 : atoi(arg
) + 2;
417 char *arg
= c
[2] ? c
+ 2 : argv
[++i
];
418 audio
= arg
[0] == '-' ? 0 : atoi(arg
) + 2;
424 int main(int argc
, char *argv
[])
427 char *path
= argv
[argc
- 1];
429 printf("usage: %s [options] filename\n", argv
[0]);
432 read_args(argc
, argv
);
434 snprintf(filename
, sizeof(filename
), "%s", path
);
435 if (video
&& !(vffs
= ffs_alloc(path
, FFS_VIDEO
| (video
- 1))))
437 if (audio
&& !(affs
= ffs_alloc(path
, FFS_AUDIO
| (audio
- 1))))
439 if (!video
&& !audio
)
444 fprintf(stderr
, "fbff: /dev/dsp busy?\n");
447 pthread_create(&a_thread
, NULL
, process_audio
, NULL
);
453 ffs_vinfo(vffs
, &w
, &h
);
454 if (magnify
!= 1 && sizeof(fbval_t
) != FBM_BPP(fb_mode()))
455 fprintf(stderr
, "fbff: magnify != 1 and fbval_t doesn't match\n");
457 float hz
= (float) fb_rows() / h
/ magnify
;
458 float wz
= (float) fb_cols() / w
/ magnify
;
459 zoom
= hz
< wz
? hz
: wz
;
461 ffs_vconf(vffs
, zoom
, fb_mode());
464 signal(SIGCONT
, sigcont
);
474 pthread_join(a_thread
, NULL
);