draw: update from fbpad
[fbff.git] / fbff.c
blob3e96ee73c50280cf35da39e5c1b29346a3e9c6bb
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 <alsa/asoundlib.h>
17 #include <libavformat/avformat.h>
18 #include <libavcodec/avcodec.h>
19 #include <libswscale/swscale.h>
20 #include "draw.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 */
41 static int arg;
42 static struct termios termios;
43 static unsigned long num; /* decoded video frame number */
44 static unsigned int vnum; /* number of successive video frames */
45 static int cmd;
47 static float zoom = 1;
48 static int magnify = 0;
49 static int drop = 0;
50 static int jump = 0;
51 static int fullscreen = 0;
52 static int audio = 1;
53 static int video = 1;
54 static int rate = 0;
56 static void init_streams(void)
58 int i;
59 for (i = 0; i < fc->nb_streams; i++) {
60 if (fc->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO)
61 vsi = i;
62 if (fc->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO)
63 asi = i;
65 if (video && vsi != -1) {
66 vcc = fc->streams[vsi]->codec;
67 vc = avcodec_find_decoder(vcc->codec_id);
68 avcodec_open(vcc, vc);
70 if (audio && asi != -1) {
71 acc = fc->streams[asi]->codec;
72 ac = avcodec_find_decoder(acc->codec_id);
73 avcodec_open(acc, ac);
75 seek_idx = vcc ? vsi : asi;
78 static void draw_frame(void)
80 fbval_t buf[1 << 14];
81 int r, c;
82 int nr = MIN(vcc->height * zoom, fb_rows() / magnify);
83 int nc = MIN(vcc->width * zoom, fb_cols() / magnify);
84 int i;
85 for (r = 0; r < nr; r++) {
86 unsigned char *row = frame->data[0] + r * frame->linesize[0];
87 for (c = 0; c < nc; c++) {
88 fbval_t v = fb_color(row[c * 3],
89 row[c * 3 + 1],
90 row[c * 3 + 2]);
91 for (i = 0; i < magnify; i++)
92 buf[c * magnify + i] = v;
94 for (i = 0; i < magnify; i++)
95 fb_set(r * magnify + i, 0, buf, nc * magnify);
99 static void decode_video_frame(AVFrame *main_frame, AVPacket *packet)
101 int fine = 0;
102 avcodec_decode_video2(vcc, main_frame, &fine, packet);
103 if (fine && (!jump || !(num % jump)) && (!drop || !vnum)) {
104 sws_scale(swsc, main_frame->data, main_frame->linesize,
105 0, vcc->height, frame->data, frame->linesize);
106 draw_frame();
110 #define AUDIOBUFSIZE (1 << 20)
112 static void decode_audio_frame(AVPacket *pkt)
114 char buf[AUDIOBUFSIZE];
115 AVPacket tmppkt;
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,
121 &size, &tmppkt);
122 if (len < 0)
123 break;
124 if (size <= 0)
125 continue;
126 if (snd_pcm_writei(alsa, buf, size / bps) < 0)
127 snd_pcm_prepare(alsa);
128 tmppkt.size -= len;
129 tmppkt.data += len;
133 static int readkey(void)
135 char b;
136 if (read(STDIN_FILENO, &b, 1) <= 0)
137 return -1;
138 return b;
141 static void waitkey(void)
143 struct pollfd ufds[1];
144 ufds[0].fd = STDIN_FILENO;
145 ufds[0].events = POLLIN;
146 poll(ufds, 1, -1);
149 static int ffarg(void)
151 int n = arg;
152 arg = 0;
153 return n ? n : 1;
156 static void ffjmp(int n, int rel)
158 pos_cur = rel ? pos_cur + n * frame_jmp : pos_cur * n / 100;
159 if (pos_cur < 0)
160 pos_cur = 0;
161 if (pos_cur > pos_max)
162 pos_max = pos_cur;
163 av_seek_frame(fc, seek_idx, pos_cur,
164 frame_jmp == 1 ? AVSEEK_FLAG_FRAME : 0);
167 static void printinfo(void)
169 printf("fbff: %d\t%d \r", pos_cur, pos_cur * 100 / pos_max);
170 fflush(stdout);
173 #define JMP1 (1 << 5)
174 #define JMP2 (JMP1 << 3)
175 #define JMP3 (JMP2 << 5)
177 #define FF_PLAY 0
178 #define FF_PAUSE 1
179 #define FF_EXIT 2
181 static void execkey(void)
183 int c;
184 while ((c = readkey()) != -1) {
185 switch (c) {
186 case 'q':
187 cmd = FF_EXIT;
188 break;
189 case 'l':
190 ffjmp(ffarg() * JMP1, 1);
191 break;
192 case 'h':
193 ffjmp(-ffarg() * JMP1, 1);
194 break;
195 case 'j':
196 ffjmp(ffarg() * JMP2, 1);
197 break;
198 case 'k':
199 ffjmp(-ffarg() * JMP2, 1);
200 break;
201 case 'J':
202 ffjmp(ffarg() * JMP3, 1);
203 break;
204 case 'K':
205 ffjmp(-ffarg() * JMP3, 1);
206 break;
207 case '%':
208 if (arg)
209 ffjmp(100, 0);
210 break;
211 case 'i':
212 printinfo();
213 break;
214 case ' ':
215 case 'p':
216 cmd = cmd ? FF_PLAY : FF_PAUSE;
217 break;
218 case 27:
219 arg = 0;
220 break;
221 default:
222 if (isdigit(c))
223 arg = arg * 10 + c - '0';
228 static void read_frames(void)
230 AVFrame *main_frame = avcodec_alloc_frame();
231 AVPacket pkt;
232 uint8_t *buf;
233 int n = AUDIOBUFSIZE;
234 if (vcc)
235 n = avpicture_get_size(PIX_FMT_RGB24, vcc->width * zoom,
236 vcc->height * zoom);
237 buf = av_malloc(n * sizeof(uint8_t));
238 if (vcc)
239 avpicture_fill((AVPicture *) frame, buf, PIX_FMT_RGB24,
240 vcc->width * zoom, vcc->height * zoom);
241 while (cmd != FF_EXIT && av_read_frame(fc, &pkt) >= 0) {
242 execkey();
243 if (cmd == FF_PAUSE) {
244 waitkey();
245 continue;
247 if (vcc && pkt.stream_index == vsi) {
248 if (rate)
249 usleep(1000000 / rate);
250 decode_video_frame(main_frame, &pkt);
251 vnum++;
252 num++;
254 if (acc && pkt.stream_index == asi) {
255 decode_audio_frame(&pkt);
256 vnum = 0;
258 if (pkt.stream_index == seek_idx) {
259 pos_cur += frame_jmp;
260 if (pos_cur > pos_max)
261 pos_max = pos_cur;
263 av_free_packet(&pkt);
265 av_free(buf);
266 av_free(main_frame);
269 #define ALSADEV "default"
271 static void alsa_init(void)
273 int format = SND_PCM_FORMAT_S16_LE;
274 if (snd_pcm_open(&alsa, ALSADEV, SND_PCM_STREAM_PLAYBACK, 0) < 0)
275 return;
276 snd_pcm_set_params(alsa, format, SND_PCM_ACCESS_RW_INTERLEAVED,
277 acc->channels, acc->sample_rate, 1, 500000);
278 bps = acc->channels * snd_pcm_format_physical_width(format) / 8;
279 snd_pcm_prepare(alsa);
282 static void alsa_close(void)
284 snd_pcm_close(alsa);
287 static void term_setup(void)
289 struct termios newtermios;
290 tcgetattr(STDIN_FILENO, &termios);
291 newtermios = termios;
292 newtermios.c_lflag &= ~ICANON;
293 newtermios.c_lflag &= ~ECHO;
294 tcsetattr(STDIN_FILENO, TCSAFLUSH, &newtermios);
295 fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | O_NONBLOCK);
298 static void term_cleanup(void)
300 tcsetattr(STDIN_FILENO, 0, &termios);
303 static void sigcont(int sig)
305 term_setup();
308 static char *usage = "usage: fbff [options] file\n"
309 "\noptions:\n"
310 " -m x magnify the screen by repeating pixels\n"
311 " -z x zoom the screen using ffmpeg\n"
312 " -j x jump every x video frames; for slow machines\n"
313 " -d don't draw following video frames\n"
314 " -f start full screen\n"
315 " -r x set the fps; for video only playback\n"
316 " -v video only playback\n"
317 " -a audio only playback\n"
318 " -t use time based seeking; only if the default does't work\n";
320 static void read_args(int argc, char *argv[])
322 int i = 1;
323 while (i < argc) {
324 if (!strcmp(argv[i], "-m"))
325 magnify = atoi(argv[++i]);
326 if (!strcmp(argv[i], "-z"))
327 zoom = atof(argv[++i]);
328 if (!strcmp(argv[i], "-j"))
329 jump = atoi(argv[++i]);
330 if (!strcmp(argv[i], "-d"))
331 drop = 1;
332 if (!strcmp(argv[i], "-f"))
333 fullscreen = 1;
334 if (!strcmp(argv[i], "-r"))
335 rate = atoi(argv[++i]);
336 if (!strcmp(argv[i], "-a"))
337 video = 0;
338 if (!strcmp(argv[i], "-v"))
339 audio = 0;
340 if (!strcmp(argv[i], "-t"))
341 frame_jmp = 1024 / 32;
342 if (!strcmp(argv[i], "-h"))
343 printf(usage);
344 i++;
348 int main(int argc, char *argv[])
350 if (argc < 2) {
351 printf("usage: %s [options] filename\n", argv[0]);
352 return 1;
354 read_args(argc, argv);
355 av_register_all();
356 if (av_open_input_file(&fc, argv[argc - 1], NULL, 0, NULL))
357 return 1;
358 if (av_find_stream_info(fc) < 0)
359 return 1;
360 init_streams();
361 frame = avcodec_alloc_frame();
362 if (acc)
363 alsa_init();
364 if (vcc) {
365 fb_init();
366 if (!magnify)
367 magnify = fb_cols() / vcc->width / zoom;
368 if (fullscreen)
369 zoom = (float) fb_cols() / vcc->width / magnify;
370 swsc = sws_getContext(vcc->width, vcc->height, vcc->pix_fmt,
371 vcc->width * zoom, vcc->height * zoom,
372 PIX_FMT_RGB24, SWS_FAST_BILINEAR | SWS_CPU_CAPS_MMX2,
373 NULL, NULL, NULL);
376 term_setup();
377 signal(SIGCONT, sigcont);
378 read_frames();
379 term_cleanup();
381 if (vcc) {
382 fb_free();
383 sws_freeContext(swsc);
384 avcodec_close(vcc);
386 if (acc) {
387 alsa_close();
388 avcodec_close(acc);
390 av_free(frame);
391 av_close_input_file(fc);
392 return 0;