basic seeking
[fbff.git] / fbff.c
blob7bf06d1edc3349e2f4f7f31bb71802559f67f3a3
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 int ffarg(void)
118 int n = arg;
119 arg = 0;
120 return n ? n : 1;
123 static void ffjmp(int t)
125 int64_t n = pts * 1000000.0 * av_q2d(fc->streams[vsi]->time_base)
126 / 1000000000.0;
127 av_seek_frame(fc, -1, (n + t) * AV_TIME_BASE, AVSEEK_FLAG_ANY);
130 #define SHORTJMP (1 << 3)
131 #define NORMJMP (SHORTJMP << 4)
132 #define LONGJMP (NORMJMP << 4)
134 static int execkey(void)
136 int c;
137 while ((c = readkey()) != -1) {
138 switch (c) {
139 case 'q':
140 return 1;
141 case 'l':
142 ffjmp(ffarg() * SHORTJMP);
143 break;
144 case 'h':
145 ffjmp(-ffarg() * SHORTJMP);
146 break;
147 case 'j':
148 ffjmp(ffarg() * NORMJMP);
149 break;
150 case 'k':
151 ffjmp(-ffarg() * NORMJMP);
152 break;
153 case 'J':
154 ffjmp(ffarg() * LONGJMP);
155 break;
156 case 'K':
157 ffjmp(-ffarg() * LONGJMP);
158 break;
159 case 27:
160 arg = 0;
161 break;
162 default:
163 if (isdigit(c))
164 arg = arg * 10 + c - '0';
167 return 0;
170 static void read_frames(void)
172 AVFrame *main_frame = avcodec_alloc_frame();
173 AVPacket pkt;
174 uint8_t *buf;
175 int n = AUDIOBUFSIZE;
176 if (vcc)
177 n = avpicture_get_size(PIX_FMT_RGB24, vcc->width * ZOOM,
178 vcc->height * ZOOM);
179 buf = av_malloc(n * sizeof(uint8_t));
180 if (vcc)
181 avpicture_fill((AVPicture *) frame, buf, PIX_FMT_RGB24,
182 vcc->width * ZOOM, vcc->height * ZOOM);
183 while (av_read_frame(fc, &pkt) >= 0) {
184 if (pts < pkt.pts)
185 pts = pkt.pts;
186 if (vcc && pkt.stream_index == vsi)
187 decode_video_frame(main_frame, &pkt);
188 if (acc && pkt.stream_index == asi)
189 decode_audio_frame(&pkt);
190 av_free_packet(&pkt);
191 if (execkey())
192 break;
194 av_free(buf);
195 av_free(main_frame);
198 #define ALSADEV "default"
200 static void alsa_init(void)
202 int format = SND_PCM_FORMAT_S16_LE;
203 if (snd_pcm_open(&alsa, ALSADEV, SND_PCM_STREAM_PLAYBACK, 0) < 0)
204 return;
205 snd_pcm_set_params(alsa, format, SND_PCM_ACCESS_RW_INTERLEAVED,
206 acc->channels, acc->sample_rate, 1, 500000);
207 bps = acc->channels * snd_pcm_format_physical_width(format) / 8;
208 snd_pcm_prepare(alsa);
211 static void alsa_close(void)
213 snd_pcm_close(alsa);
216 static void term_setup(void)
218 struct termios newtermios;
219 tcgetattr(STDIN_FILENO, &termios);
220 newtermios = termios;
221 cfmakeraw(&newtermios);
222 tcsetattr(STDIN_FILENO, TCSAFLUSH, &newtermios);
223 fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | O_NONBLOCK);
226 static void term_cleanup(void)
228 tcsetattr(STDIN_FILENO, 0, &termios);
231 int main(int argc, char *argv[])
233 if (argc < 2) {
234 printf("usage: %s filename\n", argv[0]);
235 return 1;
237 av_register_all();
238 if (av_open_input_file(&fc, argv[1], NULL, 0, NULL))
239 return 1;
240 if (av_find_stream_info(fc) < 0)
241 return 1;
242 init_streams();
243 frame = avcodec_alloc_frame();
244 if (acc)
245 alsa_init();
246 if (vcc) {
247 swsc = sws_getContext(vcc->width, vcc->height, vcc->pix_fmt,
248 vcc->width * ZOOM, vcc->height * ZOOM,
249 PIX_FMT_RGB24, SWS_FAST_BILINEAR | SWS_CPU_CAPS_MMX2,
250 NULL, NULL, NULL);
251 fb_init();
254 term_setup();
255 read_frames();
256 term_cleanup();
258 if (vcc) {
259 fb_free();
260 sws_freeContext(swsc);
261 avcodec_close(vcc);
263 if (acc) {
264 alsa_close();
265 avcodec_close(acc);
267 av_free(frame);
268 av_close_input_file(fc);
269 return 0;