fix draw_frame() to work for all fb depths
[fbff.git] / fbff.c
blobe9b864579a9a43cb3a7a7fa1fcadc2a971f0bee6
1 /*
2 * fbff - a small ffmpeg-based framebuffer/alsa media player
4 * Copyright (C) 2009-2011 Ali Gholami Rudi
6 * This program is released under GNU GPL version 2.
7 */
8 #include <fcntl.h>
9 #include <pty.h>
10 #include <signal.h>
11 #include <stdint.h>
12 #include <termios.h>
13 #include <unistd.h>
14 #include <sys/time.h>
15 #include <alsa/asoundlib.h>
16 #include <libavformat/avformat.h>
17 #include <libavcodec/avcodec.h>
18 #include <libswscale/swscale.h>
19 #include "config.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 int cmd;
45 static long last_ts;
47 static float zoom = 1;
48 static int magnify = 0;
49 static int jump = 0;
50 static int fullscreen = 0;
51 static int audio = 1;
52 static int video = 1;
53 static int just = 0;
55 static void init_streams(void)
57 int i;
58 for (i = 0; i < fc->nb_streams; i++) {
59 if (fc->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO)
60 vsi = i;
61 if (fc->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO)
62 asi = i;
64 if (video && vsi != -1) {
65 vcc = fc->streams[vsi]->codec;
66 vc = avcodec_find_decoder(vcc->codec_id);
67 avcodec_open(vcc, vc);
69 if (audio && asi != -1) {
70 acc = fc->streams[asi]->codec;
71 ac = avcodec_find_decoder(acc->codec_id);
72 avcodec_open(acc, ac);
74 seek_idx = vcc ? vsi : asi;
77 static void draw_frame(void)
79 fbval_t buf[1 << 14];
80 int r, c;
81 int nr = MIN(vcc->height * zoom, fb_rows() / magnify);
82 int nc = MIN(vcc->width * zoom, fb_cols() / magnify);
83 int cb = just ? fb_cols() - nc * magnify : 0;
84 int i;
85 for (r = 0; r < nr; r++) {
86 fbval_t *row = (void *) frame->data[0] + r * frame->linesize[0];
87 if (magnify == 1) {
88 fb_set(r, cb, (void *) row, nc);
89 continue;
91 for (c = 0; c < nc; c++)
92 for (i = 0; i < magnify; i++)
93 buf[c * magnify + i] = row[c];
94 for (i = 0; i < magnify; i++)
95 fb_set(r * magnify + i, cb, 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))) {
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);
165 last_ts = 0;
168 static void printinfo(void)
170 printf("fbff: %d\t%d \r", pos_cur, pos_cur * 100 / pos_max);
171 fflush(stdout);
174 #define JMP1 (1 << 5)
175 #define JMP2 (JMP1 << 3)
176 #define JMP3 (JMP2 << 5)
178 #define FF_PLAY 0
179 #define FF_PAUSE 1
180 #define FF_EXIT 2
182 static void execkey(void)
184 int c;
185 while ((c = readkey()) != -1) {
186 switch (c) {
187 case 'q':
188 cmd = FF_EXIT;
189 break;
190 case 'l':
191 ffjmp(ffarg() * JMP1, 1);
192 break;
193 case 'h':
194 ffjmp(-ffarg() * JMP1, 1);
195 break;
196 case 'j':
197 ffjmp(ffarg() * JMP2, 1);
198 break;
199 case 'k':
200 ffjmp(-ffarg() * JMP2, 1);
201 break;
202 case 'J':
203 ffjmp(ffarg() * JMP3, 1);
204 break;
205 case 'K':
206 ffjmp(-ffarg() * JMP3, 1);
207 break;
208 case '%':
209 if (arg)
210 ffjmp(100, 0);
211 break;
212 case 'i':
213 printinfo();
214 break;
215 case ' ':
216 case 'p':
217 cmd = cmd ? FF_PLAY : FF_PAUSE;
218 break;
219 case 27:
220 arg = 0;
221 break;
222 default:
223 if (isdigit(c))
224 arg = arg * 10 + c - '0';
229 static long ts_ms(void)
231 struct timeval tv;
232 gettimeofday(&tv, NULL);
233 return tv.tv_sec * 1000 + tv.tv_usec / 1000;
236 static void wait(long ts, int vdelay)
238 int nts = ts_ms();
239 if (ts + vdelay > nts)
240 usleep((ts + vdelay - nts) * 1000);
243 static void read_frames(void)
245 AVFrame *main_frame = avcodec_alloc_frame();
246 AVPacket pkt;
247 uint8_t *buf;
248 int n = AUDIOBUFSIZE;
249 if (vcc)
250 n = avpicture_get_size(FFMPEG_PIXFMT, vcc->width * zoom,
251 vcc->height * zoom);
252 buf = av_malloc(n * sizeof(uint8_t));
253 if (vcc)
254 avpicture_fill((AVPicture *) frame, buf, FFMPEG_PIXFMT,
255 vcc->width * zoom, vcc->height * zoom);
256 while (cmd != FF_EXIT && av_read_frame(fc, &pkt) >= 0) {
257 execkey();
258 if (cmd == FF_PAUSE) {
259 waitkey();
260 continue;
262 if (vcc && pkt.stream_index == vsi) {
263 if (!audio && last_ts) {
264 AVRational *r = &fc->streams[vsi]->time_base;
265 int vdelay = 1000 * r->num / r->den;
266 wait(last_ts, vdelay);
268 last_ts = ts_ms();
269 decode_video_frame(main_frame, &pkt);
270 num++;
272 if (acc && pkt.stream_index == asi)
273 decode_audio_frame(&pkt);
274 if (pkt.stream_index == seek_idx) {
275 pos_cur += frame_jmp;
276 if (pos_cur > pos_max)
277 pos_max = pos_cur;
279 av_free_packet(&pkt);
281 av_free(buf);
282 av_free(main_frame);
285 #define ALSADEV "default"
287 static void alsa_init(void)
289 int format = SND_PCM_FORMAT_S16_LE;
290 if (snd_pcm_open(&alsa, ALSADEV, SND_PCM_STREAM_PLAYBACK, 0) < 0)
291 return;
292 snd_pcm_set_params(alsa, format, SND_PCM_ACCESS_RW_INTERLEAVED,
293 acc->channels, acc->sample_rate, 1, 500000);
294 bps = acc->channels * snd_pcm_format_physical_width(format) / 8;
295 snd_pcm_prepare(alsa);
298 static void alsa_close(void)
300 snd_pcm_close(alsa);
303 static void term_setup(void)
305 struct termios newtermios;
306 tcgetattr(STDIN_FILENO, &termios);
307 newtermios = termios;
308 newtermios.c_lflag &= ~ICANON;
309 newtermios.c_lflag &= ~ECHO;
310 tcsetattr(STDIN_FILENO, TCSAFLUSH, &newtermios);
311 fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | O_NONBLOCK);
314 static void term_cleanup(void)
316 tcsetattr(STDIN_FILENO, 0, &termios);
319 static void sigcont(int sig)
321 term_setup();
324 static char *usage = "usage: fbff [options] file\n"
325 "\noptions:\n"
326 " -m x magnify the screen by repeating pixels\n"
327 " -z x zoom the screen using ffmpeg\n"
328 " -j x jump every x video frames; for slow machines\n"
329 " -f start full screen\n"
330 " -v video only playback\n"
331 " -a audio only playback\n"
332 " -t use time based seeking; only if the default does't work\n"
333 " -R adjust the video to the right of the screen\n\n";
335 static void read_args(int argc, char *argv[])
337 int i = 1;
338 while (i < argc) {
339 if (!strcmp(argv[i], "-m"))
340 magnify = atoi(argv[++i]);
341 if (!strcmp(argv[i], "-z"))
342 zoom = atof(argv[++i]);
343 if (!strcmp(argv[i], "-j"))
344 jump = atoi(argv[++i]);
345 if (!strcmp(argv[i], "-f"))
346 fullscreen = 1;
347 if (!strcmp(argv[i], "-a"))
348 video = 0;
349 if (!strcmp(argv[i], "-v"))
350 audio = 0;
351 if (!strcmp(argv[i], "-t"))
352 frame_jmp = 1024 / 32;
353 if (!strcmp(argv[i], "-h"))
354 printf(usage);
355 if (!strcmp(argv[i], "-R"))
356 just = 1;
357 i++;
361 int main(int argc, char *argv[])
363 if (argc < 2) {
364 printf("usage: %s [options] filename\n", argv[0]);
365 return 1;
367 read_args(argc, argv);
368 av_register_all();
369 if (av_open_input_file(&fc, argv[argc - 1], NULL, 0, NULL))
370 return 1;
371 if (av_find_stream_info(fc) < 0)
372 return 1;
373 init_streams();
374 frame = avcodec_alloc_frame();
375 if (acc)
376 alsa_init();
377 if (vcc) {
378 fb_init();
379 if (!magnify)
380 magnify = fb_cols() / vcc->width / zoom;
381 if (fullscreen)
382 zoom = (float) fb_cols() / vcc->width / magnify;
383 swsc = sws_getContext(vcc->width, vcc->height, vcc->pix_fmt,
384 vcc->width * zoom, vcc->height * zoom,
385 FFMPEG_PIXFMT, SWS_FAST_BILINEAR | SWS_CPU_CAPS_MMX2,
386 NULL, NULL, NULL);
388 term_setup();
389 signal(SIGCONT, sigcont);
390 read_frames();
391 term_cleanup();
393 if (vcc) {
394 fb_free();
395 sws_freeContext(swsc);
396 avcodec_close(vcc);
398 if (acc) {
399 alsa_close();
400 avcodec_close(acc);
402 av_free(frame);
403 av_close_input_file(fc);
404 return 0;