fbff: more accurate seeking and vi-like marks
[fbff.git] / ffs.c
blobdb25df8dcb56eb1fa0191c262acc81ae71ac515d
1 #include <fcntl.h>
2 #include <stdlib.h>
3 #include <stdint.h>
4 #include <unistd.h>
5 #include <sys/time.h>
6 #include <libavcodec/avcodec.h>
7 #include <libavformat/avformat.h>
8 #include <libswresample/swresample.h>
9 #include <libswscale/swscale.h>
10 #include "ffs.h"
12 #define FFS_SAMPLEFMT AV_SAMPLE_FMT_S16
13 #define FFS_CHLAYOUT AV_CH_LAYOUT_STEREO
15 #define MAX(a, b) ((a) < (b) ? (b) : (a))
16 #define MIN(a, b) ((a) < (b) ? (a) : (b))
18 /* ffmpeg stream */
19 struct ffs {
20 AVCodecContext *cc;
21 AVFormatContext *fc;
22 AVStream *st;
23 AVPacket pkt;
24 int si; /* stream index */
25 long ts; /* frame timestamp (ms) */
26 long pts; /* last decoded packet pts in milliseconds */
27 long dur; /* last decoded packet duration */
29 /* decoding video frames */
30 struct SwsContext *swsc;
31 struct SwrContext *swrc;
32 AVFrame *dst;
33 AVFrame *tmp;
36 struct ffs *ffs_alloc(char *path, int flags)
38 struct ffs *ffs;
39 int type = flags & FFS_VIDEO ? AVMEDIA_TYPE_VIDEO : AVMEDIA_TYPE_AUDIO;
40 int idx = (flags & FFS_STRIDX) - 1;
41 AVDictionary *opt = NULL;
42 ffs = malloc(sizeof(*ffs));
43 memset(ffs, 0, sizeof(*ffs));
44 ffs->si = -1;
45 if (avformat_open_input(&ffs->fc, path, NULL, NULL))
46 goto failed;
47 if (avformat_find_stream_info(ffs->fc, NULL) < 0)
48 goto failed;
49 ffs->si = av_find_best_stream(ffs->fc, type, idx, -1, NULL, 0);
50 if (ffs->si < 0)
51 goto failed;
52 ffs->cc = ffs->fc->streams[ffs->si]->codec;
53 if (avcodec_open2(ffs->cc, avcodec_find_decoder(ffs->cc->codec_id), &opt))
54 goto failed;
55 ffs->st = ffs->fc->streams[ffs->si];
56 ffs->tmp = av_frame_alloc();
57 ffs->dst = av_frame_alloc();
58 return ffs;
59 failed:
60 ffs_free(ffs);
61 return NULL;
64 void ffs_free(struct ffs *ffs)
66 if (ffs->swrc)
67 swr_free(&ffs->swrc);
68 if (ffs->swsc)
69 sws_freeContext(ffs->swsc);
70 if (ffs->dst)
71 av_free(ffs->dst);
72 if (ffs->tmp)
73 av_free(ffs->tmp);
74 if (ffs->cc)
75 avcodec_close(ffs->cc);
76 if (ffs->fc)
77 avformat_close_input(&ffs->fc);
78 free(ffs);
81 static AVPacket *ffs_pkt(struct ffs *ffs)
83 AVPacket *pkt = &ffs->pkt;
84 while (av_read_frame(ffs->fc, pkt) >= 0) {
85 if (pkt->stream_index == ffs->si) {
86 long pts = (pkt->dts == AV_NOPTS_VALUE ? 0 : pkt->dts) *
87 av_q2d(ffs->st->time_base) * 1000;
88 ffs->dur = MIN(MAX(0, pts - ffs->pts), 1000);
89 if (pts > ffs->pts || pts + 200 < ffs->pts)
90 ffs->pts = pts;
91 return pkt;
93 av_free_packet(pkt);
95 return NULL;
98 static long ts_ms(void)
100 struct timeval tv;
101 gettimeofday(&tv, NULL);
102 return tv.tv_sec * 1000 + tv.tv_usec / 1000;
105 static int wait(long ts, int vdelay)
107 long nts = ts_ms();
108 if (nts > ts && ts + vdelay > nts) {
109 usleep((ts + vdelay - nts) * 1000);
110 return 0;
112 return 1;
115 void ffs_wait(struct ffs *ffs)
117 int vdelay = ffs->dur;
118 if (!wait(ffs->ts, MAX(vdelay, 20)))
119 ffs->ts += MAX(vdelay, 20);
120 else
121 ffs->ts = ts_ms(); /* out of sync */
124 /* audio/video frame offset difference */
125 int ffs_avdiff(struct ffs *ffs, struct ffs *affs)
127 return affs->pts - ffs->pts;
130 long ffs_pos(struct ffs *ffs)
132 return ffs->pts;
135 void ffs_seek(struct ffs *ffs, struct ffs *vffs, long pos)
137 av_seek_frame(ffs->fc, vffs->si,
138 pos / av_q2d(vffs->st->time_base) / 1000, 0);
139 ffs->ts = 0;
142 void ffs_vinfo(struct ffs *ffs, int *w, int *h)
144 *h = ffs->cc->height;
145 *w = ffs->cc->width;
148 void ffs_ainfo(struct ffs *ffs, int *rate, int *bps, int *ch)
150 *rate = ffs->cc->sample_rate;
151 *ch = av_get_channel_layout_nb_channels(FFS_CHLAYOUT);
152 *bps = 16;
155 int ffs_vdec(struct ffs *ffs, void **buf)
157 AVCodecContext *vcc = ffs->cc;
158 AVPacket *pkt = ffs_pkt(ffs);
159 int fine = 0;
160 if (!pkt)
161 return -1;
162 avcodec_decode_video2(vcc, ffs->tmp, &fine, pkt);
163 av_free_packet(pkt);
164 if (fine && buf) {
165 sws_scale(ffs->swsc, (void *) ffs->tmp->data, ffs->tmp->linesize,
166 0, vcc->height, ffs->dst->data, ffs->dst->linesize);
167 *buf = (void *) ffs->dst->data[0];
168 return ffs->dst->linesize[0];
170 return 0;
173 static int ffs_bytespersample(ffs)
175 return av_get_bytes_per_sample(FFS_SAMPLEFMT) *
176 av_get_channel_layout_nb_channels(FFS_CHLAYOUT);
179 int ffs_adec(struct ffs *ffs, void *buf, int blen)
181 int rdec = 0;
182 AVPacket tmppkt = {0};
183 AVPacket *pkt = ffs_pkt(ffs);
184 uint8_t *out[] = {NULL};
185 if (!pkt)
186 return -1;
187 tmppkt.size = pkt->size;
188 tmppkt.data = pkt->data;
189 while (tmppkt.size > 0) {
190 int len, size;
191 len = avcodec_decode_audio4(ffs->cc, ffs->tmp, &size, &tmppkt);
192 if (len < 0)
193 break;
194 tmppkt.size -= len;
195 tmppkt.data += len;
196 if (size <= 0)
197 continue;
198 out[0] = buf + rdec;
199 len = swr_convert(ffs->swrc,
200 out, (blen - rdec) / ffs_bytespersample(ffs),
201 (void *) ffs->tmp->extended_data, ffs->tmp->nb_samples);
202 if (len > 0)
203 rdec += len * ffs_bytespersample(ffs);
205 av_free_packet(pkt);
206 return rdec;
209 static int fbm2pixfmt(int fbm)
211 switch (fbm & 0x0fff) {
212 case 0x888:
213 return PIX_FMT_RGB32;
214 case 0x565:
215 return PIX_FMT_RGB565;
216 case 0x233:
217 return PIX_FMT_RGB8;
218 default:
219 fprintf(stderr, "ffs: unknown fb_mode()\n");
220 return PIX_FMT_RGB32;
224 void ffs_vconf(struct ffs *ffs, float zoom, int fbm)
226 int h = ffs->cc->height;
227 int w = ffs->cc->width;
228 int fmt = ffs->cc->pix_fmt;
229 int pixfmt = fbm2pixfmt(fbm);
230 uint8_t *buf = NULL;
231 int n;
232 ffs->swsc = sws_getContext(w, h, fmt, w * zoom, h * zoom,
233 pixfmt, SWS_FAST_BILINEAR | SWS_CPU_CAPS_MMX2,
234 NULL, NULL, NULL);
235 n = avpicture_get_size(pixfmt, w * zoom, h * zoom);
236 buf = av_malloc(n * sizeof(uint8_t));
237 avpicture_fill((AVPicture *) ffs->dst, buf, pixfmt, w * zoom, h * zoom);
240 void ffs_aconf(struct ffs *ffs)
242 int rate, bps, ch;
243 ffs_ainfo(ffs, &rate, &bps, &ch);
244 ffs->swrc = swr_alloc_set_opts(NULL,
245 FFS_CHLAYOUT, FFS_SAMPLEFMT, rate,
246 ffs->cc->channel_layout, ffs->cc->sample_fmt, ffs->cc->sample_rate,
247 0, NULL);
248 swr_init(ffs->swrc);
251 void ffs_globinit(void)
253 av_register_all();
254 avformat_network_init();
257 long ffs_duration(struct ffs *ffs)
259 if (ffs->st->duration == AV_NOPTS_VALUE)
260 return 0;
261 return ffs->st->duration * av_q2d(ffs->st->time_base) * 1000;