simplify fflen() to use fc->duration
[fbff.git] / fbff.c
blob3be79908583317bd1071cea3693a0bbf5f352a76
1 #include <fcntl.h>
2 #include <pty.h>
3 #include <stdint.h>
4 #include <termios.h>
5 #include <unistd.h>
6 #include <alsa/asoundlib.h>
7 #include <libavformat/avformat.h>
8 #include <libavcodec/avcodec.h>
9 #include <libswscale/swscale.h>
10 #include "draw.h"
12 #define ZOOM 1
13 #define ZOOM2 1
15 static AVFormatContext *fc;
16 static AVFrame *frame;
17 static struct SwsContext *swsc;
19 static int vsi = -1; /* video stream index */
20 static AVCodecContext *vcc; /* video codec context */
21 static AVCodec *vc; /* video codec */
23 static int asi = -1; /* audio stream index */
24 static AVCodecContext *acc; /* audio codec context */
25 static AVCodec *ac; /* audio codec */
27 static snd_pcm_t *alsa;
28 static int bps; /* bytes per sample */
29 static int arg;
30 static struct termios termios;
31 static uint64_t pts;
33 static void init_streams(void)
35 int i;
36 for (i = 0; i < fc->nb_streams; i++) {
37 if (fc->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO)
38 vsi = i;
39 if (fc->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO)
40 asi = i;
42 if (vsi != -1) {
43 vcc = fc->streams[vsi]->codec;
44 vc = avcodec_find_decoder(vcc->codec_id);
45 avcodec_open(vcc, vc);
47 if (asi != -1) {
48 acc = fc->streams[asi]->codec;
49 ac = avcodec_find_decoder(acc->codec_id);
50 avcodec_open(acc, ac);
54 static void draw_frame(void)
56 fbval_t buf[1 << 14];
57 int r, c;
58 int nr = MIN(vcc->height * ZOOM, fb_rows() / ZOOM2);
59 int nc = MIN(vcc->width * ZOOM, fb_cols() / ZOOM2);
60 int i;
61 for (r = 0; r < nr; r++) {
62 unsigned char *row = frame->data[0] + r * frame->linesize[0];
63 for (c = 0; c < nc; c++) {
64 fbval_t v = fb_color(row[c * 3],
65 row[c * 3 + 1],
66 row[c * 3 + 2]);
67 for (i = 0; i < ZOOM2; i++)
68 buf[c * ZOOM2 + i] = v;
70 for (i = 0; i < ZOOM2; i++)
71 fb_set(r * ZOOM2 + i, 0, buf, nc * ZOOM2);
75 static void decode_video_frame(AVFrame *main_frame, AVPacket *packet)
77 int fine = 0;
78 avcodec_decode_video2(vcc, main_frame, &fine, packet);
79 if (fine) {
80 sws_scale(swsc, main_frame->data, main_frame->linesize,
81 0, vcc->height, frame->data, frame->linesize);
82 draw_frame();
86 #define AUDIOBUFSIZE (1 << 20)
88 static void decode_audio_frame(AVPacket *pkt)
90 char buf[AUDIOBUFSIZE];
91 AVPacket tmppkt;
92 tmppkt.size = pkt->size;
93 tmppkt.data = pkt->data;
94 while (tmppkt.size > 0) {
95 int size = sizeof(buf);
96 int len = avcodec_decode_audio3(acc, (int16_t *) buf,
97 &size, &tmppkt);
98 if (len < 0)
99 break;
100 if (size <= 0)
101 continue;
102 if (snd_pcm_writei(alsa, buf, size / bps) < 0)
103 snd_pcm_prepare(alsa);
104 tmppkt.size -= len;
105 tmppkt.data += len;
108 static int readkey(void)
110 char b;
111 if (read(STDIN_FILENO, &b, 1) <= 0)
112 return -1;
113 return b;
116 static void waitkey(void)
118 struct pollfd ufds[1];
119 ufds[0].fd = STDIN_FILENO;
120 ufds[0].events = POLLIN;
121 poll(ufds, 1, -1);
124 static int ffarg(void)
126 int n = arg;
127 arg = 0;
128 return n ? n : 1;
131 static int ffpos(void)
133 int idx = vsi != -1 ? vsi : asi;
134 double base = av_q2d(fc->streams[idx]->time_base);
135 return pts * 1000.0 * base / 1000.0;
138 static int fflen(void)
140 return fc->duration / AV_TIME_BASE;
143 static void ffjmp(int n, int rel)
145 int t = MAX(0, MIN(fflen(), rel ? ffpos() + n : n));
146 av_seek_frame(fc, -1, t * AV_TIME_BASE, AVSEEK_FLAG_ANY);
149 static void printinfo(void)
151 int loc = (int64_t) ffpos() * 1000 / fflen();
152 int pos = ffpos();
153 printf("fbff: %3d.%d%% %8ds\r", loc / 10, loc % 10, pos);
154 fflush(stdout);
157 #define SHORTJMP (1 << 3)
158 #define NORMJMP (SHORTJMP << 4)
159 #define LONGJMP (NORMJMP << 4)
161 #define FF_PLAY 0
162 #define FF_PAUSE 1
163 #define FF_EXIT 2
165 static int execkey(void)
167 int c;
168 while ((c = readkey()) != -1) {
169 switch (c) {
170 case 'q':
171 return FF_EXIT;
172 case 'l':
173 ffjmp(ffarg() * SHORTJMP, 1);
174 break;
175 case 'h':
176 ffjmp(-ffarg() * SHORTJMP, 1);
177 break;
178 case 'j':
179 ffjmp(ffarg() * NORMJMP, 1);
180 break;
181 case 'k':
182 ffjmp(-ffarg() * NORMJMP, 1);
183 break;
184 case 'J':
185 ffjmp(ffarg() * LONGJMP, 1);
186 break;
187 case 'K':
188 ffjmp(-ffarg() * LONGJMP, 1);
189 break;
190 case '%':
191 if (arg)
192 ffjmp(ffarg() * fflen() / 100, 0);
193 break;
194 case 'i':
195 printinfo();
196 break;
197 case ' ':
198 case 'p':
199 return FF_PAUSE;
200 break;
201 case 27:
202 arg = 0;
203 break;
204 default:
205 if (isdigit(c))
206 arg = arg * 10 + c - '0';
209 return FF_PLAY;
212 static void read_frames(void)
214 AVFrame *main_frame = avcodec_alloc_frame();
215 AVPacket pkt;
216 uint8_t *buf;
217 int n = AUDIOBUFSIZE;
218 if (vcc)
219 n = avpicture_get_size(PIX_FMT_RGB24, vcc->width * ZOOM,
220 vcc->height * ZOOM);
221 buf = av_malloc(n * sizeof(uint8_t));
222 if (vcc)
223 avpicture_fill((AVPicture *) frame, buf, PIX_FMT_RGB24,
224 vcc->width * ZOOM, vcc->height * ZOOM);
225 while (av_read_frame(fc, &pkt) >= 0) {
226 if (pts < pkt.pts && pkt.pts < (1ull << 60))
227 pts = pkt.pts;
228 if (vcc && pkt.stream_index == vsi)
229 decode_video_frame(main_frame, &pkt);
230 if (acc && pkt.stream_index == asi)
231 decode_audio_frame(&pkt);
232 av_free_packet(&pkt);
233 switch (execkey()) {
234 case FF_PLAY:
235 continue;
236 case FF_PAUSE:
237 while (readkey() != 'p')
238 waitkey();
239 continue;
241 break;
243 av_free(buf);
244 av_free(main_frame);
247 #define ALSADEV "default"
249 static void alsa_init(void)
251 int format = SND_PCM_FORMAT_S16_LE;
252 if (snd_pcm_open(&alsa, ALSADEV, SND_PCM_STREAM_PLAYBACK, 0) < 0)
253 return;
254 snd_pcm_set_params(alsa, format, SND_PCM_ACCESS_RW_INTERLEAVED,
255 acc->channels, acc->sample_rate, 1, 500000);
256 bps = acc->channels * snd_pcm_format_physical_width(format) / 8;
257 snd_pcm_prepare(alsa);
260 static void alsa_close(void)
262 snd_pcm_close(alsa);
265 static void term_setup(void)
267 struct termios newtermios;
268 tcgetattr(STDIN_FILENO, &termios);
269 newtermios = termios;
270 cfmakeraw(&newtermios);
271 tcsetattr(STDIN_FILENO, TCSAFLUSH, &newtermios);
272 fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | O_NONBLOCK);
275 static void term_cleanup(void)
277 tcsetattr(STDIN_FILENO, 0, &termios);
280 int main(int argc, char *argv[])
282 if (argc < 2) {
283 printf("usage: %s filename\n", argv[0]);
284 return 1;
286 av_register_all();
287 if (av_open_input_file(&fc, argv[1], NULL, 0, NULL))
288 return 1;
289 if (av_find_stream_info(fc) < 0)
290 return 1;
291 init_streams();
292 frame = avcodec_alloc_frame();
293 if (acc)
294 alsa_init();
295 if (vcc) {
296 swsc = sws_getContext(vcc->width, vcc->height, vcc->pix_fmt,
297 vcc->width * ZOOM, vcc->height * ZOOM,
298 PIX_FMT_RGB24, SWS_FAST_BILINEAR | SWS_CPU_CAPS_MMX2,
299 NULL, NULL, NULL);
300 fb_init();
303 term_setup();
304 read_frames();
305 term_cleanup();
307 if (vcc) {
308 fb_free();
309 sws_freeContext(swsc);
310 avcodec_close(vcc);
312 if (acc) {
313 alsa_close();
314 avcodec_close(acc);
316 av_free(frame);
317 av_close_input_file(fc);
318 return 0;