frame dropping with -d arg
[fbff.git] / fbff.c
blob4a0f4afc72a7829a946f0e8fcedd1e7067441e64
1 /*
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.
9 */
10 #include <fcntl.h>
11 #include <pty.h>
12 #include <stdint.h>
13 #include <termios.h>
14 #include <unistd.h>
15 #include <alsa/asoundlib.h>
16 #include <libavformat/avformat.h>
17 #include <libavcodec/avcodec.h>
18 #include <libswscale/swscale.h>
19 #include "draw.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 */
35 static int arg;
36 static struct termios termios;
37 static uint64_t pts;
39 static int zoom = 1;
40 static int magnify = 0;
41 static int drop = 0;
43 static void init_streams(void)
45 int i;
46 for (i = 0; i < fc->nb_streams; i++) {
47 if (fc->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO)
48 vsi = i;
49 if (fc->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO)
50 asi = i;
52 if (vsi != -1) {
53 vcc = fc->streams[vsi]->codec;
54 vc = avcodec_find_decoder(vcc->codec_id);
55 avcodec_open(vcc, vc);
57 if (asi != -1) {
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)
66 fbval_t buf[1 << 14];
67 int r, c;
68 int nr = MIN(vcc->height * zoom, fb_rows() / magnify);
69 int nc = MIN(vcc->width * zoom, fb_cols() / magnify);
70 int i;
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],
75 row[c * 3 + 1],
76 row[c * 3 + 2]);
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)
87 int fine = 0;
88 avcodec_decode_video2(vcc, main_frame, &fine, packet);
89 if (fine) {
90 sws_scale(swsc, main_frame->data, main_frame->linesize,
91 0, vcc->height, frame->data, frame->linesize);
92 draw_frame();
96 #define AUDIOBUFSIZE (1 << 20)
98 static void decode_audio_frame(AVPacket *pkt)
100 char buf[AUDIOBUFSIZE];
101 AVPacket tmppkt;
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,
107 &size, &tmppkt);
108 if (len < 0)
109 break;
110 if (size <= 0)
111 continue;
112 if (snd_pcm_writei(alsa, buf, size / bps) < 0)
113 snd_pcm_prepare(alsa);
114 tmppkt.size -= len;
115 tmppkt.data += len;
118 static int readkey(void)
120 char b;
121 if (read(STDIN_FILENO, &b, 1) <= 0)
122 return -1;
123 return b;
126 static void waitkey(void)
128 struct pollfd ufds[1];
129 ufds[0].fd = STDIN_FILENO;
130 ufds[0].events = POLLIN;
131 poll(ufds, 1, -1);
134 static int ffarg(void)
136 int n = arg;
137 arg = 0;
138 return n ? n : 1;
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();
162 int pos = ffpos();
163 printf("fbff: %3d.%d%% %8ds\r", loc / 10, loc % 10, pos);
164 fflush(stdout);
167 #define SHORTJMP (1 << 3)
168 #define NORMJMP (SHORTJMP << 4)
169 #define LONGJMP (NORMJMP << 4)
171 #define FF_PLAY 0
172 #define FF_PAUSE 1
173 #define FF_EXIT 2
175 static int execkey(void)
177 int c;
178 while ((c = readkey()) != -1) {
179 switch (c) {
180 case 'q':
181 return FF_EXIT;
182 case 'l':
183 ffjmp(ffarg() * SHORTJMP, 1);
184 break;
185 case 'h':
186 ffjmp(-ffarg() * SHORTJMP, 1);
187 break;
188 case 'j':
189 ffjmp(ffarg() * NORMJMP, 1);
190 break;
191 case 'k':
192 ffjmp(-ffarg() * NORMJMP, 1);
193 break;
194 case 'J':
195 ffjmp(ffarg() * LONGJMP, 1);
196 break;
197 case 'K':
198 ffjmp(-ffarg() * LONGJMP, 1);
199 break;
200 case '%':
201 if (arg)
202 ffjmp(ffarg() * fflen() / 100, 0);
203 break;
204 case 'i':
205 printinfo();
206 break;
207 case ' ':
208 case 'p':
209 return FF_PAUSE;
210 break;
211 case 27:
212 arg = 0;
213 break;
214 default:
215 if (isdigit(c))
216 arg = arg * 10 + c - '0';
219 return FF_PLAY;
222 static void read_frames(void)
224 AVFrame *main_frame = avcodec_alloc_frame();
225 AVPacket pkt;
226 uint8_t *buf;
227 int n = AUDIOBUFSIZE;
228 int video_chain = 0;
229 if (vcc)
230 n = avpicture_get_size(PIX_FMT_RGB24, vcc->width * zoom,
231 vcc->height * zoom);
232 buf = av_malloc(n * sizeof(uint8_t));
233 if (vcc)
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))
238 pts = pkt.pts;
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);
244 video_chain = 0;
246 av_free_packet(&pkt);
247 switch (execkey()) {
248 case FF_PLAY:
249 continue;
250 case FF_PAUSE:
251 while (readkey() != 'p')
252 waitkey();
253 continue;
255 break;
257 av_free(buf);
258 av_free(main_frame);
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)
267 return;
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)
276 snd_pcm_close(alsa);
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[])
296 int i = 1;
297 while (i < argc) {
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"))
303 drop = 1;
304 i++;
308 int main(int argc, char *argv[])
310 if (argc < 2) {
311 printf("usage: %s [-z zoom] [-m magnify] [-d] filename\n",
312 argv[0]);
313 return 1;
315 read_args(argc, argv);
316 av_register_all();
317 if (av_open_input_file(&fc, argv[argc - 1], NULL, 0, NULL))
318 return 1;
319 if (av_find_stream_info(fc) < 0)
320 return 1;
321 init_streams();
322 frame = avcodec_alloc_frame();
323 if (acc)
324 alsa_init();
325 if (vcc) {
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,
329 NULL, NULL, NULL);
330 fb_init();
331 if (!magnify)
332 magnify = fb_rows() / vcc->height / zoom;
335 term_setup();
336 read_frames();
337 term_cleanup();
339 if (vcc) {
340 fb_free();
341 sws_freeContext(swsc);
342 avcodec_close(vcc);
344 if (acc) {
345 alsa_close();
346 avcodec_close(acc);
348 av_free(frame);
349 av_close_input_file(fc);
350 return 0;