build: Update for libtool-2.2 and autoconf-2.62
[2oom.git] / src / module / ffmpeg.c
blobb6552da93f861578278c91fbea604b167ad7ab8c
1 /* 2ooM: The Master of Orion II Reverse Engineering Project
2 * Copyright (C) 2006 Nick Bowler
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #define FFMPEG_MODULE "AV-FFmpeg"
20 #define FFMPEG_MOD_VERSION "0.1.0"
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <stdint.h>
28 #include <libavformat/avformat.h>
29 #include <libavcodec/avcodec.h>
30 #include <libavutil/avstring.h>
32 #include "../plugin/plugin.h"
33 #include "../av/avfile.h"
34 #include "../vfs/vfs.h"
35 #include "../sys/log.h"
37 #define init ffmpeg_LTX_init
39 #define MIN(a,b) (((a)<(b))?(a):(b))
41 static struct resource {
42 AVFormatContext *fmt;
43 AVCodecContext *actx, *vctx;
44 AVCodec *acodec, *vcodec;
45 int astream, vstream;
47 int16_t *aframe;
48 int aframelen, aframeoff;
50 struct audio_format afmt;
51 } *data;
52 static int datasz = 4;
54 static struct vfs_access vfs;
55 static struct log_context logger;
57 /* 2ooM VFS open handler for libavformat */
58 static int ff_vfs_open(URLContext *h, const char *filename, int flags)
60 struct vfs_file *f;
62 av_strstart(filename, "vfs:", &filename);
64 /* 2ooM VFS is read-only */
65 if (flags & URL_RDWR || flags & URL_WRONLY)
66 return -EPERM;
68 if (!(f = vfs.open(filename)))
69 return -ENOENT;
71 h->priv_data = f;
73 return 0;
76 static int ff_vfs_read(URLContext *h, unsigned char *buf, int size)
78 return vfs.read(h->priv_data, buf, size);
81 static int ff_vfs_write(URLContext *h, unsigned char *buf, int size)
83 return -1; /* 2ooM VFS is read-only */
86 static offset_t ff_vfs_seek(URLContext *h, offset_t pos, int whence)
88 return vfs.seek(h->priv_data, pos, whence);
91 static int ff_vfs_close(URLContext *h)
93 vfs.close(h->priv_data);
94 return 0;
97 static URLProtocol vfs_protocol = {
98 .name = "vfs",
99 .url_open = ff_vfs_open,
100 .url_read = ff_vfs_read,
101 .url_write = ff_vfs_write,
102 .url_seek = ff_vfs_seek,
103 .url_close = ff_vfs_close,
106 /* Double the size of the buffer for storing resources */
107 static int moreresources(void) {
108 static int init;
109 struct resource *new;
110 int i;
112 if (!(new = realloc(data, sizeof *data * datasz*2)))
113 return -1;
115 data = new;
117 for (i = (!init) ? init = 1, 0 : datasz; i < datasz*2; i++)
118 data[i].fmt = NULL;
120 datasz *= 2;
122 return 0;
125 /* Locate the first stream matching type, initialize codecs, and return the
126 * stream ID.
128 static int getstream(enum CodecType type, AVCodec **codec, AVFormatContext *fmt)
130 AVCodecContext *ctx;
131 int i;
133 for (i = 0; i < fmt->nb_streams; i++)
134 if (fmt->streams[i]->codec->codec_type == type)
135 break;
137 if (i >= fmt->nb_streams) {
138 logger.write(LOG_DEBUG, FFMPEG_MODULE,
139 "Failed to find%s stream in %s",
140 (type == CODEC_TYPE_AUDIO) ? " audio" :
141 (type == CODEC_TYPE_VIDEO) ? " video" : "",
142 fmt->filename);
143 return -1;
146 ctx = fmt->streams[i]->codec;
148 if (!(*codec = avcodec_find_decoder(ctx->codec_id))) {
149 logger.write(LOG_DEBUG, FFMPEG_MODULE,
150 "Failed to find decoder for %s", ctx->codec_name);
151 return -1;
154 if (avcodec_open(ctx, *codec) < 0) {
155 logger.write(LOG_DEBUG, FFMPEG_MODULE,
156 "Failed to open decoder %s", (*codec)->name);
157 return -1;
160 return i;
163 /* Attempt to open a VFS resource with FFmpeg */
164 static int ff_av_open(const char *resource, int type,
165 const struct audio_format *afmt,
166 const struct video_format *vfmt)
168 char *url;
169 struct resource r = { .astream = -1, .vstream = -1 };
170 int i;
172 if (!(type & AV_INPUT_AUDIO || type & AV_INPUT_VIDEO))
173 return -1;
175 if (!(url = malloc(strlen("vfs:") + strlen(resource) + 1)))
176 return -1;
178 /* Find a free resource, or make one */
179 for (i = 0; i < datasz; i++)
180 if (data[i].fmt == NULL)
181 break;
182 if (i >= datasz)
183 if (moreresources())
184 goto err;
186 strcpy(url, "vfs:");
187 strcat(url, resource);
189 if (av_open_input_file(&r.fmt, url, NULL, 0, NULL) != 0) {
190 logger.write(LOG_DEBUG, FFMPEG_MODULE,
191 "Failed to open %s", url);
192 goto err;
195 if (av_find_stream_info(r.fmt) < 0) {
196 logger.write(LOG_DEBUG, FFMPEG_MODULE,
197 "Failed to find stream info for %s", url);
198 goto err;
201 if (type & AV_INPUT_AUDIO) {
202 r.astream = getstream(CODEC_TYPE_AUDIO, &r.acodec, r.fmt);
203 if (r.astream == -1)
204 goto err;
205 r.actx = r.fmt->streams[r.astream]->codec;
206 r.afmt = *afmt;
207 if (!(r.aframe = malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE)))
208 goto err;
210 if (type & AV_INPUT_VIDEO) {
211 r.vstream = getstream(CODEC_TYPE_VIDEO, &r.vcodec, r.fmt);
212 if (r.vstream == -1)
213 goto err;
214 r.vctx = r.fmt->streams[r.vstream]->codec;
217 data[i] = r;
218 return i;
219 err:
220 if (r.actx)
221 avcodec_close(r.actx);
222 if (r.vctx)
223 avcodec_close(r.vctx);
224 if (r.fmt)
225 av_close_input_file(r.fmt);
227 free(r.aframe);
229 free(url);
230 return -1;
233 /* Close an open resource */
234 static void ff_av_close(int id)
236 if (id >= datasz || !data[id].fmt)
237 return;
239 if (data[id].actx)
240 avcodec_close(data[id].actx);
241 if (data[id].vctx)
242 avcodec_close(data[id].vctx);
244 av_close_input_file(data[id].fmt);
245 data[id].fmt = NULL;
247 free(data[id].aframe);
249 return;
252 /* Read decoded audio data */
253 static int ff_av_read_audio(int id, void *buf, int len)
255 struct resource *d;
256 int written = 0;
257 char *stream = buf;
259 if (id >= datasz || !data[id].fmt || data[id].astream == -1)
260 return -1;
262 d = &data[id];
264 if (d->aframeoff < d->aframelen) {
265 if (d->aframelen - d->aframeoff >= len) {
266 memcpy(stream, d->aframe+(d->aframeoff/2), len);
267 d->aframeoff += len;
268 written += len;
269 return written;
270 } else {
271 memcpy(stream, d->aframe+(d->aframeoff/2),
272 d->aframelen - d->aframeoff);
274 written += d->aframelen - d->aframeoff;
275 len -= d->aframelen - d->aframeoff;
276 stream += d->aframelen - d->aframeoff;
280 while (len > 0) {
281 AVPacket packet;
282 d->aframelen = 0, d->aframeoff = 0;
283 if (av_read_frame(d->fmt, &packet) < 0)
284 break;
286 if (packet.stream_index == d->astream) {
287 d->aframelen = AVCODEC_MAX_AUDIO_FRAME_SIZE;
288 avcodec_decode_audio2(d->actx, d->aframe, &d->aframelen,
289 packet.data, packet.size);
291 if (d->aframelen <= 0)
292 return av_free_packet(&packet), written;
294 memcpy(stream, d->aframe, MIN(len, d->aframelen));
296 d->aframeoff += MIN(len, d->aframelen);
297 stream += MIN(len, d->aframelen);
298 written += MIN(len, d->aframelen);
299 len -= MIN(len, d->aframelen);
302 av_free_packet(&packet);
305 return written;
308 static int ff_av_rewind(int id)
310 if (id >= datasz || !data[id].fmt)
311 return -1;
313 if (av_seek_frame(data[id].fmt, -1, 0, AVSEEK_FLAG_BYTE) < 0)
314 return -1;
316 return 0;
319 int init(plugin_useservice_t useservice)
321 static struct av_accessor av = {
322 .open = ff_av_open,
323 .close = ff_av_close,
324 .read_audio = ff_av_read_audio,
325 .rewind = ff_av_rewind,
328 if (useservice(LOG_SERVICE, FFMPEG_MODULE, &logger))
329 return -1;
331 if (useservice(VFSACCESS_SERVICE, FFMPEG_MODULE, &vfs))
332 return -1;
334 if (useservice(AV_PLUGIN_SERVICE, FFMPEG_MODULE, &av))
335 return -1;
337 if (moreresources())
338 return -1;
340 av_register_all();
341 register_protocol(&vfs_protocol);
343 logger.write(LOG_INFO, FFMPEG_MODULE,
344 "libavformat/libavcodec media input " FFMPEG_MOD_VERSION);
346 return 0;