2 * fbff - a small ffmpeg-based framebuffer/alsa media player
4 * Copyright (C) 2009-2011 Ali Gholami Rudi
6 * This program is released under GNU GPL version 2.
15 #include <alsa/asoundlib.h>
16 #include <libavformat/avformat.h>
17 #include <libavcodec/avcodec.h>
18 #include <libswscale/swscale.h>
22 static AVFormatContext
*fc
;
23 static AVFrame
*frame
;
24 static struct SwsContext
*swsc
;
26 static int vsi
= -1; /* video stream index */
27 static AVCodecContext
*vcc
; /* video codec context */
28 static AVCodec
*vc
; /* video codec */
30 static int asi
= -1; /* audio stream index */
31 static AVCodecContext
*acc
; /* audio codec context */
32 static AVCodec
*ac
; /* audio codec */
34 static int seek_idx
; /* stream index used for seeking */
35 static int pos_cur
; /* current frame number in seek_idx stream */
36 static int pos_max
; /* maximum frame number seen so far */
37 static int frame_jmp
= 1; /* the changes to pos_cur for each frame */
39 static snd_pcm_t
*alsa
;
40 static int bps
; /* bytes per sample */
42 static struct termios termios
;
43 static unsigned long num
; /* decoded video frame number */
47 static float zoom
= 1;
48 static int magnify
= 0;
50 static int fullscreen
= 0;
55 static void init_streams(void)
58 for (i
= 0; i
< fc
->nb_streams
; i
++) {
59 if (fc
->streams
[i
]->codec
->codec_type
== CODEC_TYPE_VIDEO
)
61 if (fc
->streams
[i
]->codec
->codec_type
== CODEC_TYPE_AUDIO
)
64 if (video
&& vsi
!= -1) {
65 vcc
= fc
->streams
[vsi
]->codec
;
66 vc
= avcodec_find_decoder(vcc
->codec_id
);
67 avcodec_open(vcc
, vc
);
69 if (audio
&& asi
!= -1) {
70 acc
= fc
->streams
[asi
]->codec
;
71 ac
= avcodec_find_decoder(acc
->codec_id
);
72 avcodec_open(acc
, ac
);
74 seek_idx
= vcc
? vsi
: asi
;
77 static void draw_frame(void)
81 int nr
= MIN(vcc
->height
* zoom
, fb_rows() / magnify
);
82 int nc
= MIN(vcc
->width
* zoom
, fb_cols() / magnify
);
83 int cb
= just
? fb_cols() - nc
* magnify
: 0;
85 for (r
= 0; r
< nr
; r
++) {
86 fbval_t
*row
= (void *) frame
->data
[0] + r
* frame
->linesize
[0];
88 fb_set(r
, cb
, (void *) row
, nc
);
91 for (c
= 0; c
< nc
; c
++)
92 for (i
= 0; i
< magnify
; i
++)
93 buf
[c
* magnify
+ i
] = row
[c
];
94 for (i
= 0; i
< magnify
; i
++)
95 fb_set(r
* magnify
+ i
, cb
, buf
, nc
* magnify
);
99 static void decode_video_frame(AVFrame
*main_frame
, AVPacket
*packet
)
102 avcodec_decode_video2(vcc
, main_frame
, &fine
, packet
);
103 if (fine
&& (!jump
|| !(num
% jump
))) {
104 sws_scale(swsc
, main_frame
->data
, main_frame
->linesize
,
105 0, vcc
->height
, frame
->data
, frame
->linesize
);
110 #define AUDIOBUFSIZE (1 << 20)
112 static void decode_audio_frame(AVPacket
*pkt
)
114 char buf
[AUDIOBUFSIZE
];
116 tmppkt
.size
= pkt
->size
;
117 tmppkt
.data
= pkt
->data
;
118 while (tmppkt
.size
> 0) {
119 int size
= sizeof(buf
);
120 int len
= avcodec_decode_audio3(acc
, (int16_t *) buf
,
126 if (snd_pcm_writei(alsa
, buf
, size
/ bps
) < 0)
127 snd_pcm_prepare(alsa
);
133 static int readkey(void)
136 if (read(STDIN_FILENO
, &b
, 1) <= 0)
141 static void waitkey(void)
143 struct pollfd ufds
[1];
144 ufds
[0].fd
= STDIN_FILENO
;
145 ufds
[0].events
= POLLIN
;
149 static int ffarg(void)
156 static void ffjmp(int n
, int rel
)
158 pos_cur
= rel
? pos_cur
+ n
* frame_jmp
: pos_cur
* n
/ 100;
161 if (pos_cur
> pos_max
)
163 av_seek_frame(fc
, seek_idx
, pos_cur
,
164 frame_jmp
== 1 ? AVSEEK_FLAG_FRAME
: 0);
168 static void printinfo(void)
170 printf("fbff: %d\t%d \r", pos_cur
, pos_cur
* 100 / pos_max
);
174 #define JMP1 (1 << 5)
175 #define JMP2 (JMP1 << 3)
176 #define JMP3 (JMP2 << 5)
182 static void execkey(void)
185 while ((c
= readkey()) != -1) {
191 ffjmp(ffarg() * JMP1
, 1);
194 ffjmp(-ffarg() * JMP1
, 1);
197 ffjmp(ffarg() * JMP2
, 1);
200 ffjmp(-ffarg() * JMP2
, 1);
203 ffjmp(ffarg() * JMP3
, 1);
206 ffjmp(-ffarg() * JMP3
, 1);
217 cmd
= cmd
? FF_PLAY
: FF_PAUSE
;
224 arg
= arg
* 10 + c
- '0';
229 static long ts_ms(void)
232 gettimeofday(&tv
, NULL
);
233 return tv
.tv_sec
* 1000 + tv
.tv_usec
/ 1000;
236 static void wait(long ts
, int vdelay
)
239 if (ts
+ vdelay
> nts
)
240 usleep((ts
+ vdelay
- nts
) * 1000);
243 static void read_frames(void)
245 AVFrame
*main_frame
= avcodec_alloc_frame();
248 int n
= AUDIOBUFSIZE
;
250 n
= avpicture_get_size(FFMPEG_PIXFMT
, vcc
->width
* zoom
,
252 buf
= av_malloc(n
* sizeof(uint8_t));
254 avpicture_fill((AVPicture
*) frame
, buf
, FFMPEG_PIXFMT
,
255 vcc
->width
* zoom
, vcc
->height
* zoom
);
256 while (cmd
!= FF_EXIT
&& av_read_frame(fc
, &pkt
) >= 0) {
258 if (cmd
== FF_PAUSE
) {
262 if (vcc
&& pkt
.stream_index
== vsi
) {
263 if (!audio
&& last_ts
) {
264 AVRational
*r
= &fc
->streams
[vsi
]->time_base
;
265 int vdelay
= 1000 * r
->num
/ r
->den
;
266 wait(last_ts
, vdelay
);
269 decode_video_frame(main_frame
, &pkt
);
272 if (acc
&& pkt
.stream_index
== asi
)
273 decode_audio_frame(&pkt
);
274 if (pkt
.stream_index
== seek_idx
) {
275 pos_cur
+= frame_jmp
;
276 if (pos_cur
> pos_max
)
279 av_free_packet(&pkt
);
285 #define ALSADEV "default"
287 static void alsa_init(void)
289 int format
= SND_PCM_FORMAT_S16_LE
;
290 if (snd_pcm_open(&alsa
, ALSADEV
, SND_PCM_STREAM_PLAYBACK
, 0) < 0)
292 snd_pcm_set_params(alsa
, format
, SND_PCM_ACCESS_RW_INTERLEAVED
,
293 acc
->channels
, acc
->sample_rate
, 1, 500000);
294 bps
= acc
->channels
* snd_pcm_format_physical_width(format
) / 8;
295 snd_pcm_prepare(alsa
);
298 static void alsa_close(void)
303 static void term_setup(void)
305 struct termios newtermios
;
306 tcgetattr(STDIN_FILENO
, &termios
);
307 newtermios
= termios
;
308 newtermios
.c_lflag
&= ~ICANON
;
309 newtermios
.c_lflag
&= ~ECHO
;
310 tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &newtermios
);
311 fcntl(STDIN_FILENO
, F_SETFL
, fcntl(STDIN_FILENO
, F_GETFL
) | O_NONBLOCK
);
314 static void term_cleanup(void)
316 tcsetattr(STDIN_FILENO
, 0, &termios
);
319 static void sigcont(int sig
)
324 static char *usage
= "usage: fbff [options] file\n"
326 " -m x magnify the screen by repeating pixels\n"
327 " -z x zoom the screen using ffmpeg\n"
328 " -j x jump every x video frames; for slow machines\n"
329 " -f start full screen\n"
330 " -v video only playback\n"
331 " -a audio only playback\n"
332 " -t use time based seeking; only if the default does't work\n"
333 " -R adjust the video to the right of the screen\n\n";
335 static void read_args(int argc
, char *argv
[])
339 if (!strcmp(argv
[i
], "-m"))
340 magnify
= atoi(argv
[++i
]);
341 if (!strcmp(argv
[i
], "-z"))
342 zoom
= atof(argv
[++i
]);
343 if (!strcmp(argv
[i
], "-j"))
344 jump
= atoi(argv
[++i
]);
345 if (!strcmp(argv
[i
], "-f"))
347 if (!strcmp(argv
[i
], "-a"))
349 if (!strcmp(argv
[i
], "-v"))
351 if (!strcmp(argv
[i
], "-t"))
352 frame_jmp
= 1024 / 32;
353 if (!strcmp(argv
[i
], "-h"))
355 if (!strcmp(argv
[i
], "-R"))
361 int main(int argc
, char *argv
[])
364 printf("usage: %s [options] filename\n", argv
[0]);
367 read_args(argc
, argv
);
369 if (av_open_input_file(&fc
, argv
[argc
- 1], NULL
, 0, NULL
))
371 if (av_find_stream_info(fc
) < 0)
374 frame
= avcodec_alloc_frame();
380 magnify
= fb_cols() / vcc
->width
/ zoom
;
382 zoom
= (float) fb_cols() / vcc
->width
/ magnify
;
383 swsc
= sws_getContext(vcc
->width
, vcc
->height
, vcc
->pix_fmt
,
384 vcc
->width
* zoom
, vcc
->height
* zoom
,
385 FFMPEG_PIXFMT
, SWS_FAST_BILINEAR
| SWS_CPU_CAPS_MMX2
,
389 signal(SIGCONT
, sigcont
);
395 sws_freeContext(swsc
);
403 av_close_input_file(fc
);