2 * fbff - A small ffmpeg-based framebuffer/alsa media player
4 * Copyright (C) 2009 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.
15 #include <alsa/asoundlib.h>
16 #include <libavformat/avformat.h>
17 #include <libavcodec/avcodec.h>
18 #include <libswscale/swscale.h>
21 static AVFormatContext
*fc
;
22 static AVFrame
*frame
;
23 static struct SwsContext
*swsc
;
25 static int vsi
= -1; /* video stream index */
26 static AVCodecContext
*vcc
; /* video codec context */
27 static AVCodec
*vc
; /* video codec */
29 static int asi
= -1; /* audio stream index */
30 static AVCodecContext
*acc
; /* audio codec context */
31 static AVCodec
*ac
; /* audio codec */
33 static snd_pcm_t
*alsa
;
34 static int bps
; /* bytes per sample */
36 static struct termios termios
;
40 static int magnify
= 0;
43 static void init_streams(void)
46 for (i
= 0; i
< fc
->nb_streams
; i
++) {
47 if (fc
->streams
[i
]->codec
->codec_type
== CODEC_TYPE_VIDEO
)
49 if (fc
->streams
[i
]->codec
->codec_type
== CODEC_TYPE_AUDIO
)
53 vcc
= fc
->streams
[vsi
]->codec
;
54 vc
= avcodec_find_decoder(vcc
->codec_id
);
55 avcodec_open(vcc
, vc
);
58 acc
= fc
->streams
[asi
]->codec
;
59 ac
= avcodec_find_decoder(acc
->codec_id
);
60 avcodec_open(acc
, ac
);
64 static void draw_frame(void)
68 int nr
= MIN(vcc
->height
* zoom
, fb_rows() / magnify
);
69 int nc
= MIN(vcc
->width
* zoom
, fb_cols() / magnify
);
71 for (r
= 0; r
< nr
; r
++) {
72 unsigned char *row
= frame
->data
[0] + r
* frame
->linesize
[0];
73 for (c
= 0; c
< nc
; c
++) {
74 fbval_t v
= fb_color(row
[c
* 3],
77 for (i
= 0; i
< magnify
; i
++)
78 buf
[c
* magnify
+ i
] = v
;
80 for (i
= 0; i
< magnify
; i
++)
81 fb_set(r
* magnify
+ i
, 0, buf
, nc
* magnify
);
85 static void decode_video_frame(AVFrame
*main_frame
, AVPacket
*packet
)
88 avcodec_decode_video2(vcc
, main_frame
, &fine
, packet
);
90 sws_scale(swsc
, main_frame
->data
, main_frame
->linesize
,
91 0, vcc
->height
, frame
->data
, frame
->linesize
);
96 #define AUDIOBUFSIZE (1 << 20)
98 static void decode_audio_frame(AVPacket
*pkt
)
100 char buf
[AUDIOBUFSIZE
];
102 tmppkt
.size
= pkt
->size
;
103 tmppkt
.data
= pkt
->data
;
104 while (tmppkt
.size
> 0) {
105 int size
= sizeof(buf
);
106 int len
= avcodec_decode_audio3(acc
, (int16_t *) buf
,
112 if (snd_pcm_writei(alsa
, buf
, size
/ bps
) < 0)
113 snd_pcm_prepare(alsa
);
118 static int readkey(void)
121 if (read(STDIN_FILENO
, &b
, 1) <= 0)
126 static void waitkey(void)
128 struct pollfd ufds
[1];
129 ufds
[0].fd
= STDIN_FILENO
;
130 ufds
[0].events
= POLLIN
;
134 static int ffarg(void)
141 static int ffpos(void)
143 int idx
= vsi
!= -1 ? vsi
: asi
;
144 double base
= av_q2d(fc
->streams
[idx
]->time_base
);
145 return pts
* 1000.0 * base
/ 1000.0;
148 static int fflen(void)
150 return fc
->duration
/ AV_TIME_BASE
;
153 static void ffjmp(int n
, int rel
)
155 int t
= MAX(0, MIN(fflen(), rel
? ffpos() + n
: n
));
156 av_seek_frame(fc
, -1, t
* AV_TIME_BASE
, AVSEEK_FLAG_ANY
);
159 static void printinfo(void)
161 int loc
= (int64_t) ffpos() * 1000 / fflen();
163 printf("fbff: %3d.%d%% %8ds\r", loc
/ 10, loc
% 10, pos
);
167 #define SHORTJMP (1 << 3)
168 #define NORMJMP (SHORTJMP << 4)
169 #define LONGJMP (NORMJMP << 4)
175 static int execkey(void)
178 while ((c
= readkey()) != -1) {
183 ffjmp(ffarg() * SHORTJMP
, 1);
186 ffjmp(-ffarg() * SHORTJMP
, 1);
189 ffjmp(ffarg() * NORMJMP
, 1);
192 ffjmp(-ffarg() * NORMJMP
, 1);
195 ffjmp(ffarg() * LONGJMP
, 1);
198 ffjmp(-ffarg() * LONGJMP
, 1);
202 ffjmp(ffarg() * fflen() / 100, 0);
216 arg
= arg
* 10 + c
- '0';
222 static void read_frames(void)
224 AVFrame
*main_frame
= avcodec_alloc_frame();
227 int n
= AUDIOBUFSIZE
;
230 n
= avpicture_get_size(PIX_FMT_RGB24
, vcc
->width
* zoom
,
232 buf
= av_malloc(n
* sizeof(uint8_t));
234 avpicture_fill((AVPicture
*) frame
, buf
, PIX_FMT_RGB24
,
235 vcc
->width
* zoom
, vcc
->height
* zoom
);
236 while (av_read_frame(fc
, &pkt
) >= 0) {
237 if (pts
< pkt
.pts
&& pkt
.pts
< (1ull << 60))
239 if (vcc
&& pkt
.stream_index
== vsi
)
240 if (!drop
|| !video_chain
++)
241 decode_video_frame(main_frame
, &pkt
);
242 if (acc
&& pkt
.stream_index
== asi
) {
243 decode_audio_frame(&pkt
);
246 av_free_packet(&pkt
);
251 while (readkey() != 'p')
261 #define ALSADEV "default"
263 static void alsa_init(void)
265 int format
= SND_PCM_FORMAT_S16_LE
;
266 if (snd_pcm_open(&alsa
, ALSADEV
, SND_PCM_STREAM_PLAYBACK
, 0) < 0)
268 snd_pcm_set_params(alsa
, format
, SND_PCM_ACCESS_RW_INTERLEAVED
,
269 acc
->channels
, acc
->sample_rate
, 1, 500000);
270 bps
= acc
->channels
* snd_pcm_format_physical_width(format
) / 8;
271 snd_pcm_prepare(alsa
);
274 static void alsa_close(void)
279 static void term_setup(void)
281 struct termios newtermios
;
282 tcgetattr(STDIN_FILENO
, &termios
);
283 newtermios
= termios
;
284 cfmakeraw(&newtermios
);
285 tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &newtermios
);
286 fcntl(STDIN_FILENO
, F_SETFL
, fcntl(STDIN_FILENO
, F_GETFL
) | O_NONBLOCK
);
289 static void term_cleanup(void)
291 tcsetattr(STDIN_FILENO
, 0, &termios
);
294 static void read_args(int argc
, char *argv
[])
298 if (!strcmp(argv
[i
], "-m"))
299 magnify
= atoi(argv
[++i
]);
300 if (!strcmp(argv
[i
], "-z"))
301 zoom
= atoi(argv
[++i
]);
302 if (!strcmp(argv
[i
], "-d"))
308 int main(int argc
, char *argv
[])
311 printf("usage: %s [-z zoom] [-m magnify] [-d] filename\n",
315 read_args(argc
, argv
);
317 if (av_open_input_file(&fc
, argv
[argc
- 1], NULL
, 0, NULL
))
319 if (av_find_stream_info(fc
) < 0)
322 frame
= avcodec_alloc_frame();
326 swsc
= sws_getContext(vcc
->width
, vcc
->height
, vcc
->pix_fmt
,
327 vcc
->width
* zoom
, vcc
->height
* zoom
,
328 PIX_FMT_RGB24
, SWS_FAST_BILINEAR
| SWS_CPU_CAPS_MMX2
,
332 magnify
= fb_rows() / vcc
->height
/ zoom
;
341 sws_freeContext(swsc
);
349 av_close_input_file(fc
);