Makefile: more generic compiler flags
[fbff.git] / fbff.c
blob59436661a8d409457fbb8a9ff672f0bba364e6b2
1 /*
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.
9 */
10 #include <fcntl.h>
11 #include <pty.h>
12 #include <signal.h>
13 #include <stdint.h>
14 #include <termios.h>
15 #include <unistd.h>
16 #include <sys/time.h>
17 #include <alsa/asoundlib.h>
18 #include <libavformat/avformat.h>
19 #include <libavcodec/avcodec.h>
20 #include <libswscale/swscale.h>
21 #include "draw.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 */
42 static int arg;
43 static struct termios termios;
44 static unsigned long num; /* decoded video frame number */
45 static unsigned int vnum; /* number of successive video frames */
46 static int cmd;
47 static long last_ts;
49 static float zoom = 1;
50 static int magnify = 0;
51 static int drop = 0;
52 static int jump = 0;
53 static int fullscreen = 0;
54 static int audio = 1;
55 static int video = 1;
56 static int just = 0;
58 static void init_streams(void)
60 int i;
61 for (i = 0; i < fc->nb_streams; i++) {
62 if (fc->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO)
63 vsi = i;
64 if (fc->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO)
65 asi = i;
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)
82 fbval_t buf[1 << 14];
83 int r, c;
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;
87 int i;
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],
92 row[c * 3 + 1],
93 row[c * 3 + 2]);
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)
104 int fine = 0;
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);
109 draw_frame();
113 #define AUDIOBUFSIZE (1 << 20)
115 static void decode_audio_frame(AVPacket *pkt)
117 char buf[AUDIOBUFSIZE];
118 AVPacket tmppkt;
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,
124 &size, &tmppkt);
125 if (len < 0)
126 break;
127 if (size <= 0)
128 continue;
129 if (snd_pcm_writei(alsa, buf, size / bps) < 0)
130 snd_pcm_prepare(alsa);
131 tmppkt.size -= len;
132 tmppkt.data += len;
136 static int readkey(void)
138 char b;
139 if (read(STDIN_FILENO, &b, 1) <= 0)
140 return -1;
141 return b;
144 static void waitkey(void)
146 struct pollfd ufds[1];
147 ufds[0].fd = STDIN_FILENO;
148 ufds[0].events = POLLIN;
149 poll(ufds, 1, -1);
152 static int ffarg(void)
154 int n = arg;
155 arg = 0;
156 return n ? n : 1;
159 static void ffjmp(int n, int rel)
161 pos_cur = rel ? pos_cur + n * frame_jmp : pos_cur * n / 100;
162 if (pos_cur < 0)
163 pos_cur = 0;
164 if (pos_cur > pos_max)
165 pos_max = pos_cur;
166 av_seek_frame(fc, seek_idx, pos_cur,
167 frame_jmp == 1 ? AVSEEK_FLAG_FRAME : 0);
168 last_ts = 0;
171 static void printinfo(void)
173 printf("fbff: %d\t%d \r", pos_cur, pos_cur * 100 / pos_max);
174 fflush(stdout);
177 #define JMP1 (1 << 5)
178 #define JMP2 (JMP1 << 3)
179 #define JMP3 (JMP2 << 5)
181 #define FF_PLAY 0
182 #define FF_PAUSE 1
183 #define FF_EXIT 2
185 static void execkey(void)
187 int c;
188 while ((c = readkey()) != -1) {
189 switch (c) {
190 case 'q':
191 cmd = FF_EXIT;
192 break;
193 case 'l':
194 ffjmp(ffarg() * JMP1, 1);
195 break;
196 case 'h':
197 ffjmp(-ffarg() * JMP1, 1);
198 break;
199 case 'j':
200 ffjmp(ffarg() * JMP2, 1);
201 break;
202 case 'k':
203 ffjmp(-ffarg() * JMP2, 1);
204 break;
205 case 'J':
206 ffjmp(ffarg() * JMP3, 1);
207 break;
208 case 'K':
209 ffjmp(-ffarg() * JMP3, 1);
210 break;
211 case '%':
212 if (arg)
213 ffjmp(100, 0);
214 break;
215 case 'i':
216 printinfo();
217 break;
218 case ' ':
219 case 'p':
220 cmd = cmd ? FF_PLAY : FF_PAUSE;
221 break;
222 case 27:
223 arg = 0;
224 break;
225 default:
226 if (isdigit(c))
227 arg = arg * 10 + c - '0';
232 static long ts_ms(void)
234 struct timeval tv;
235 gettimeofday(&tv, NULL);
236 return tv.tv_sec * 1000 + tv.tv_usec / 1000;
239 static void wait(long ts, int vdelay)
241 int nts = ts_ms();
242 if (ts + vdelay > nts)
243 usleep((ts + vdelay - nts) * 1000);
246 static void read_frames(void)
248 AVFrame *main_frame = avcodec_alloc_frame();
249 AVPacket pkt;
250 uint8_t *buf;
251 int n = AUDIOBUFSIZE;
252 if (vcc)
253 n = avpicture_get_size(PIX_FMT_RGB24, vcc->width * zoom,
254 vcc->height * zoom);
255 buf = av_malloc(n * sizeof(uint8_t));
256 if (vcc)
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) {
260 execkey();
261 if (cmd == FF_PAUSE) {
262 waitkey();
263 continue;
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);
271 last_ts = ts_ms();
272 decode_video_frame(main_frame, &pkt);
273 vnum++;
274 num++;
276 if (acc && pkt.stream_index == asi) {
277 decode_audio_frame(&pkt);
278 vnum = 0;
280 if (pkt.stream_index == seek_idx) {
281 pos_cur += frame_jmp;
282 if (pos_cur > pos_max)
283 pos_max = pos_cur;
285 av_free_packet(&pkt);
287 av_free(buf);
288 av_free(main_frame);
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)
297 return;
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)
306 snd_pcm_close(alsa);
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)
327 term_setup();
330 static char *usage = "usage: fbff [options] file\n"
331 "\noptions:\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[])
344 int i = 1;
345 while (i < argc) {
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"))
353 drop = 1;
354 if (!strcmp(argv[i], "-f"))
355 fullscreen = 1;
356 if (!strcmp(argv[i], "-a"))
357 video = 0;
358 if (!strcmp(argv[i], "-v"))
359 audio = 0;
360 if (!strcmp(argv[i], "-t"))
361 frame_jmp = 1024 / 32;
362 if (!strcmp(argv[i], "-h"))
363 printf(usage);
364 if (!strcmp(argv[i], "-R"))
365 just = 1;
366 i++;
370 int main(int argc, char *argv[])
372 if (argc < 2) {
373 printf("usage: %s [options] filename\n", argv[0]);
374 return 1;
376 read_args(argc, argv);
377 av_register_all();
378 if (av_open_input_file(&fc, argv[argc - 1], NULL, 0, NULL))
379 return 1;
380 if (av_find_stream_info(fc) < 0)
381 return 1;
382 init_streams();
383 frame = avcodec_alloc_frame();
384 if (acc)
385 alsa_init();
386 if (vcc) {
387 fb_init();
388 if (!magnify)
389 magnify = fb_cols() / vcc->width / zoom;
390 if (fullscreen)
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,
395 NULL, NULL, NULL);
397 term_setup();
398 signal(SIGCONT, sigcont);
399 read_frames();
400 term_cleanup();
402 if (vcc) {
403 fb_free();
404 sws_freeContext(swsc);
405 avcodec_close(vcc);
407 if (acc) {
408 alsa_close();
409 avcodec_close(acc);
411 av_free(frame);
412 av_close_input_file(fc);
413 return 0;