2 * fbff - a small ffmpeg-based framebuffer/oss media player
4 * Copyright (C) 2009-2012 Ali Gholami Rudi
6 * This program is released under GNU GPL version 2.
19 #include <sys/soundcard.h>
25 #define MIN(a, b) ((a) < (b) ? (a) : (b))
26 #define MAX(a, b) ((a) > (b) ? (a) : (b))
29 static struct termios termios
;
33 static float zoom
= 1;
34 static int magnify
= 1;
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)
60 static void draw_frame(void *img
, int linelen
)
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
;
74 fb_set(rb
+ r
, cb
, row
, nc
);
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)
89 static char a_buf
[AUDIOBUFS
][ABUFSZ
];
90 static int a_len
[AUDIOBUFS
];
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
)
106 while (audio
&& a_reset
)
110 static int readkey(void)
113 if (read(STDIN_FILENO
, &b
, 1) <= 0)
118 static void waitkey(void)
120 struct pollfd ufds
[1];
121 ufds
[0].fd
= STDIN_FILENO
;
122 ufds
[0].events
= POLLIN
;
126 static int ffarg(int def
)
133 static void ffjmp(int n
, int rel
)
135 struct ffs
*ffs
= video
? vffs
: affs
;
136 long pos
= ffs_pos(ffs
, n
);
140 ffs_seek(affs
, pos
, frame_jmp
);
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);
153 #define JMP1 (1 << 5)
154 #define JMP2 (JMP1 << 3)
155 #define JMP3 (JMP2 << 5)
157 static void execkey(void)
160 while ((c
= readkey()) != -1) {
166 ffjmp(ffarg(1) * JMP1
, 1);
169 ffjmp(-ffarg(1) * JMP1
, 1);
172 ffjmp(ffarg(1) * JMP2
, 1);
175 ffjmp(-ffarg(1) * JMP2
, 1);
178 ffjmp(ffarg(1) * JMP3
, 1);
181 ffjmp(-ffarg(1) * JMP3
, 1);
196 sync_diff
= -ffarg(0);
199 sync_diff
= ffarg(0);
202 sync_diff
= ffs_avdiff(vffs
, affs
);
208 sync_cur
= ffarg(sync_cnt
);
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
) {
229 if (sync_first
< vnum
) {
231 sync_diff
= ffs_avdiff(vffs
, affs
);
236 return ffs_avdiff(vffs
, affs
) >= sync_diff
;
242 static void mainloop(void)
245 while (eof
< audio
+ video
) {
254 while (audio
&& !eof
&& !a_prodwait()) {
255 int ret
= ffs_adec(affs
, a_buf
[a_prod
], ABUFSZ
);
260 a_prod
= (a_prod
+ 1) & (AUDIOBUFS
- 1);
263 if (video
&& (!audio
|| eof
|| vsync())) {
264 int ignore
= jump
&& (vnum
% (jump
+ 1));
266 int ret
= ffs_vdec(vffs
, ignore
? NULL
: &buf
);
271 draw_frame((void *) buf
, ret
);
279 static void oss_init(void)
282 afd
= open("/dev/dsp", O_RDWR
);
284 fprintf(stderr
, "cannot open /dev/dsp\n");
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)
298 static void *process_audio(void *dat
)
302 while (!a_reset
&& (a_conswait() || paused
) && !exited
)
312 write(afd
, a_buf
[a_cons
], a_len
[a_cons
]);
313 a_cons
= (a_cons
+ 1) & (AUDIOBUFS
- 1);
320 static void term_setup(void)
322 struct termios newtermios
;
323 tcgetattr(STDIN_FILENO
, &termios
);
324 newtermios
= termios
;
325 newtermios
.c_lflag
&= ~ICANON
;
326 newtermios
.c_lflag
&= ~ECHO
;
327 tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &newtermios
);
328 fcntl(STDIN_FILENO
, F_SETFL
, fcntl(STDIN_FILENO
, F_GETFL
) | O_NONBLOCK
);
331 static void term_cleanup(void)
333 tcsetattr(STDIN_FILENO
, 0, &termios
);
336 static void sigcont(int sig
)
341 static char *usage
= "usage: fbff [options] file\n"
343 " -z x zoom the screen using ffmpeg\n"
344 " -m x magnify the screen by repeating pixels\n"
345 " -j x jump every x video frames; for slow machines\n"
346 " -f start full screen\n"
347 " -v x select video stream; '-' disables video\n"
348 " -a x select audio stream; '-' disables audio\n"
349 " -s always synchronize; useful for files with bad video framerate\n"
350 " -u record avdiff after a few frames\n"
351 " -t use time based seeking; only if the default does't work\n"
352 " -r adjust the video to the right of the screen\n"
353 " -b adjust the video to the bottom of the screen\n\n";
355 static void read_args(int argc
, char *argv
[])
363 magnify
= c
[2] ? atoi(c
+ 2) : atoi(argv
[++i
]);
365 zoom
= c
[2] ? atof(c
+ 2) : atof(argv
[++i
]);
367 jump
= c
[2] ? atoi(c
+ 2) : atoi(argv
[++i
]);
371 sync_period
= c
[2] ? atoi(c
+ 2) : 1;
383 char *arg
= c
[2] ? c
+ 2 : argv
[++i
];
384 video
= arg
[0] == '-' ? 0 : atoi(arg
) + 2;
387 char *arg
= c
[2] ? c
+ 2 : argv
[++i
];
388 audio
= arg
[0] == '-' ? 0 : atoi(arg
) + 2;
394 int main(int argc
, char *argv
[])
397 char *path
= argv
[argc
- 1];
399 printf("usage: %s [options] filename\n", argv
[0]);
402 read_args(argc
, argv
);
404 if (video
&& !(vffs
= ffs_alloc(path
, FFS_VIDEO
| (video
- 1))))
406 if (audio
&& !(affs
= ffs_alloc(path
, FFS_AUDIO
| (audio
- 1))))
408 if (!video
&& !audio
)
411 pthread_create(&a_thread
, NULL
, process_audio
, NULL
);
416 ffs_vinfo(vffs
, &w
, &h
);
417 if (magnify
!= 1 && sizeof(fbval_t
) != FBM_BPP(fb_mode()))
418 fprintf(stderr
, "fbff: magnify != 1 and fbval_t doesn't match\n");
420 float hz
= (float) fb_rows() / h
/ magnify
;
421 float wz
= (float) fb_cols() / w
/ magnify
;
422 zoom
= hz
< wz
? hz
: wz
;
424 ffs_vsetup(vffs
, zoom
, fb_mode());
427 signal(SIGCONT
, sigcont
);
436 pthread_join(a_thread
, NULL
);