2 * fbff - A small ffmpeg-based framebuffer/alsa media player
4 * Copyright (C) 2009-2010 Ali Gholami Rudi
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License, as published by the
8 * Free Software Foundation.
17 #include <alsa/asoundlib.h>
18 #include <libavformat/avformat.h>
19 #include <libavcodec/avcodec.h>
20 #include <libswscale/swscale.h>
23 static AVFormatContext
*fc
;
24 static AVFrame
*frame
;
25 static struct SwsContext
*swsc
;
27 static int vsi
= -1; /* video stream index */
28 static AVCodecContext
*vcc
; /* video codec context */
29 static AVCodec
*vc
; /* video codec */
31 static int asi
= -1; /* audio stream index */
32 static AVCodecContext
*acc
; /* audio codec context */
33 static AVCodec
*ac
; /* audio codec */
35 static int seek_idx
; /* stream index used for seeking */
36 static int pos_cur
; /* current frame number in seek_idx stream */
37 static int pos_max
; /* maximum frame number seen so far */
38 static int frame_jmp
= 1; /* the changes to pos_cur for each frame */
40 static snd_pcm_t
*alsa
;
41 static int bps
; /* bytes per sample */
43 static struct termios termios
;
44 static unsigned long num
; /* decoded video frame number */
45 static unsigned int vnum
; /* number of successive video frames */
49 static float zoom
= 1;
50 static int magnify
= 0;
53 static int fullscreen
= 0;
58 static void init_streams(void)
61 for (i
= 0; i
< fc
->nb_streams
; i
++) {
62 if (fc
->streams
[i
]->codec
->codec_type
== CODEC_TYPE_VIDEO
)
64 if (fc
->streams
[i
]->codec
->codec_type
== CODEC_TYPE_AUDIO
)
67 if (video
&& vsi
!= -1) {
68 vcc
= fc
->streams
[vsi
]->codec
;
69 vc
= avcodec_find_decoder(vcc
->codec_id
);
70 avcodec_open(vcc
, vc
);
72 if (audio
&& asi
!= -1) {
73 acc
= fc
->streams
[asi
]->codec
;
74 ac
= avcodec_find_decoder(acc
->codec_id
);
75 avcodec_open(acc
, ac
);
77 seek_idx
= vcc
? vsi
: asi
;
80 static void draw_frame(void)
84 int nr
= MIN(vcc
->height
* zoom
, fb_rows() / magnify
);
85 int nc
= MIN(vcc
->width
* zoom
, fb_cols() / magnify
);
86 int cb
= just
? fb_cols() - nc
* magnify
: 0;
88 for (r
= 0; r
< nr
; r
++) {
89 unsigned char *row
= frame
->data
[0] + r
* frame
->linesize
[0];
90 for (c
= 0; c
< nc
; c
++) {
91 fbval_t v
= fb_color(row
[c
* 3],
94 for (i
= 0; i
< magnify
; i
++)
95 buf
[c
* magnify
+ i
] = v
;
97 for (i
= 0; i
< magnify
; i
++)
98 fb_set(r
* magnify
+ i
, cb
, buf
, nc
* magnify
);
102 static void decode_video_frame(AVFrame
*main_frame
, AVPacket
*packet
)
105 avcodec_decode_video2(vcc
, main_frame
, &fine
, packet
);
106 if (fine
&& (!jump
|| !(num
% jump
)) && (!drop
|| !vnum
)) {
107 sws_scale(swsc
, main_frame
->data
, main_frame
->linesize
,
108 0, vcc
->height
, frame
->data
, frame
->linesize
);
113 #define AUDIOBUFSIZE (1 << 20)
115 static void decode_audio_frame(AVPacket
*pkt
)
117 char buf
[AUDIOBUFSIZE
];
119 tmppkt
.size
= pkt
->size
;
120 tmppkt
.data
= pkt
->data
;
121 while (tmppkt
.size
> 0) {
122 int size
= sizeof(buf
);
123 int len
= avcodec_decode_audio3(acc
, (int16_t *) buf
,
129 if (snd_pcm_writei(alsa
, buf
, size
/ bps
) < 0)
130 snd_pcm_prepare(alsa
);
136 static int readkey(void)
139 if (read(STDIN_FILENO
, &b
, 1) <= 0)
144 static void waitkey(void)
146 struct pollfd ufds
[1];
147 ufds
[0].fd
= STDIN_FILENO
;
148 ufds
[0].events
= POLLIN
;
152 static int ffarg(void)
159 static void ffjmp(int n
, int rel
)
161 pos_cur
= rel
? pos_cur
+ n
* frame_jmp
: pos_cur
* n
/ 100;
164 if (pos_cur
> pos_max
)
166 av_seek_frame(fc
, seek_idx
, pos_cur
,
167 frame_jmp
== 1 ? AVSEEK_FLAG_FRAME
: 0);
171 static void printinfo(void)
173 printf("fbff: %d\t%d \r", pos_cur
, pos_cur
* 100 / pos_max
);
177 #define JMP1 (1 << 5)
178 #define JMP2 (JMP1 << 3)
179 #define JMP3 (JMP2 << 5)
185 static void execkey(void)
188 while ((c
= readkey()) != -1) {
194 ffjmp(ffarg() * JMP1
, 1);
197 ffjmp(-ffarg() * JMP1
, 1);
200 ffjmp(ffarg() * JMP2
, 1);
203 ffjmp(-ffarg() * JMP2
, 1);
206 ffjmp(ffarg() * JMP3
, 1);
209 ffjmp(-ffarg() * JMP3
, 1);
220 cmd
= cmd
? FF_PLAY
: FF_PAUSE
;
227 arg
= arg
* 10 + c
- '0';
232 static long ts_ms(void)
235 gettimeofday(&tv
, NULL
);
236 return tv
.tv_sec
* 1000 + tv
.tv_usec
/ 1000;
239 static void wait(long ts
, int vdelay
)
242 if (ts
+ vdelay
> nts
)
243 usleep((ts
+ vdelay
- nts
) * 1000);
246 static void read_frames(void)
248 AVFrame
*main_frame
= avcodec_alloc_frame();
251 int n
= AUDIOBUFSIZE
;
253 n
= avpicture_get_size(PIX_FMT_RGB24
, vcc
->width
* zoom
,
255 buf
= av_malloc(n
* sizeof(uint8_t));
257 avpicture_fill((AVPicture
*) frame
, buf
, PIX_FMT_RGB24
,
258 vcc
->width
* zoom
, vcc
->height
* zoom
);
259 while (cmd
!= FF_EXIT
&& av_read_frame(fc
, &pkt
) >= 0) {
261 if (cmd
== FF_PAUSE
) {
265 if (vcc
&& pkt
.stream_index
== vsi
) {
266 if (!audio
&& last_ts
) {
267 AVRational
*r
= &fc
->streams
[vsi
]->time_base
;
268 int vdelay
= 1000 * r
->num
/ r
->den
;
269 wait(last_ts
, vdelay
);
272 decode_video_frame(main_frame
, &pkt
);
276 if (acc
&& pkt
.stream_index
== asi
) {
277 decode_audio_frame(&pkt
);
280 if (pkt
.stream_index
== seek_idx
) {
281 pos_cur
+= frame_jmp
;
282 if (pos_cur
> pos_max
)
285 av_free_packet(&pkt
);
291 #define ALSADEV "default"
293 static void alsa_init(void)
295 int format
= SND_PCM_FORMAT_S16_LE
;
296 if (snd_pcm_open(&alsa
, ALSADEV
, SND_PCM_STREAM_PLAYBACK
, 0) < 0)
298 snd_pcm_set_params(alsa
, format
, SND_PCM_ACCESS_RW_INTERLEAVED
,
299 acc
->channels
, acc
->sample_rate
, 1, 500000);
300 bps
= acc
->channels
* snd_pcm_format_physical_width(format
) / 8;
301 snd_pcm_prepare(alsa
);
304 static void alsa_close(void)
309 static void term_setup(void)
311 struct termios newtermios
;
312 tcgetattr(STDIN_FILENO
, &termios
);
313 newtermios
= termios
;
314 newtermios
.c_lflag
&= ~ICANON
;
315 newtermios
.c_lflag
&= ~ECHO
;
316 tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &newtermios
);
317 fcntl(STDIN_FILENO
, F_SETFL
, fcntl(STDIN_FILENO
, F_GETFL
) | O_NONBLOCK
);
320 static void term_cleanup(void)
322 tcsetattr(STDIN_FILENO
, 0, &termios
);
325 static void sigcont(int sig
)
330 static char *usage
= "usage: fbff [options] file\n"
332 " -m x magnify the screen by repeating pixels\n"
333 " -z x zoom the screen using ffmpeg\n"
334 " -j x jump every x video frames; for slow machines\n"
335 " -d don't draw following video frames\n"
336 " -f start full screen\n"
337 " -v video only playback\n"
338 " -a audio only playback\n"
339 " -t use time based seeking; only if the default does't work\n"
340 " -R adjust the video to the right of the screen\n\n";
342 static void read_args(int argc
, char *argv
[])
346 if (!strcmp(argv
[i
], "-m"))
347 magnify
= atoi(argv
[++i
]);
348 if (!strcmp(argv
[i
], "-z"))
349 zoom
= atof(argv
[++i
]);
350 if (!strcmp(argv
[i
], "-j"))
351 jump
= atoi(argv
[++i
]);
352 if (!strcmp(argv
[i
], "-d"))
354 if (!strcmp(argv
[i
], "-f"))
356 if (!strcmp(argv
[i
], "-a"))
358 if (!strcmp(argv
[i
], "-v"))
360 if (!strcmp(argv
[i
], "-t"))
361 frame_jmp
= 1024 / 32;
362 if (!strcmp(argv
[i
], "-h"))
364 if (!strcmp(argv
[i
], "-R"))
370 int main(int argc
, char *argv
[])
373 printf("usage: %s [options] filename\n", argv
[0]);
376 read_args(argc
, argv
);
378 if (av_open_input_file(&fc
, argv
[argc
- 1], NULL
, 0, NULL
))
380 if (av_find_stream_info(fc
) < 0)
383 frame
= avcodec_alloc_frame();
389 magnify
= fb_cols() / vcc
->width
/ zoom
;
391 zoom
= (float) fb_cols() / vcc
->width
/ magnify
;
392 swsc
= sws_getContext(vcc
->width
, vcc
->height
, vcc
->pix_fmt
,
393 vcc
->width
* zoom
, vcc
->height
* zoom
,
394 PIX_FMT_RGB24
, SWS_FAST_BILINEAR
| SWS_CPU_CAPS_MMX2
,
398 signal(SIGCONT
, sigcont
);
404 sws_freeContext(swsc
);
412 av_close_input_file(fc
);