Moved src/audio to more appropriately named src/av
[2oom.git] / src / module / ffmpeg.c
blob003bd40c9bbbf65ff5e6a080cd8a6df780704f27
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
18 * $HeadURL$
19 * $Date$
20 * $Revision$
21 * $Author$
24 #define FFMPEG_MODULE "AV-FFmpeg"
25 #define FFMPEG_MOD_VERSION "0.1.0"
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <stdint.h>
33 #include <ffmpeg/avformat.h>
34 #include <ffmpeg/avcodec.h>
36 #include "../plugin/plugin.h"
37 #include "../av/avfile.h"
38 #include "../vfs/vfs.h"
39 #include "../sys/log.h"
41 #define init ffmpeg_LTX_init
43 #define MIN(a,b) (((a)<(b))?(a):(b))
45 static struct resource {
46 AVFormatContext *fmt;
47 AVCodecContext *actx, *vctx;
48 AVCodec *acodec, *vcodec;
49 int astream, vstream;
51 int16_t *aframe;
52 int aframelen, aframeoff;
54 struct audio_format afmt;
55 } *data;
56 static int datasz = 4;
58 static struct vfs_access vfs;
59 static struct log_context logger;
61 /* 2ooM VFS open handler for libavformat */
62 static int ff_vfs_open(URLContext *h, const char *filename, int flags)
64 struct vfs_file *f;
66 strstart(filename, "vfs:", &filename);
68 /* 2ooM VFS is read-only */
69 if (flags & URL_RDWR || flags & URL_WRONLY)
70 return -EPERM;
72 if (!(f = vfs.open(filename)))
73 return -ENOENT;
75 h->priv_data = f;
77 return 0;
80 static int ff_vfs_read(URLContext *h, unsigned char *buf, int size)
82 return vfs.read(h->priv_data, buf, size);
85 static int ff_vfs_write(URLContext *h, unsigned char *buf, int size)
87 return -1; /* 2ooM VFS is read-only */
90 static offset_t ff_vfs_seek(URLContext *h, offset_t pos, int whence)
92 return vfs.seek(h->priv_data, pos, whence);
95 static int ff_vfs_close(URLContext *h)
97 vfs.close(h->priv_data);
98 return 0;
101 static URLProtocol vfs_protocol = {
102 "vfs",
103 ff_vfs_open,
104 ff_vfs_read,
105 ff_vfs_write,
106 ff_vfs_seek,
107 ff_vfs_close,
108 NULL
111 /* Double the size of the buffer for storing resources */
112 static int moreresources(void) {
113 static int init;
114 struct resource *new;
115 int i;
117 if (!(new = realloc(data, sizeof *data * datasz*2)))
118 return -1;
120 data = new;
122 for (i = (!init) ? init = 1, 0 : datasz; i < datasz*2; i++)
123 data[i].fmt = NULL;
125 datasz *= 2;
127 return 0;
130 /* Locate the first stream matching type, initialize codecs, and return the
131 * stream ID.
133 static int getstream(enum CodecType type, AVCodec **codec, AVFormatContext *fmt)
135 AVCodecContext *ctx;
136 int i;
138 for (i = 0; i < fmt->nb_streams; i++)
139 if (fmt->streams[i]->codec->codec_type == type)
140 break;
142 if (i >= fmt->nb_streams) {
143 logger.write(LOG_DEBUG, FFMPEG_MODULE,
144 "Failed to find%s stream in %s",
145 (type == CODEC_TYPE_AUDIO) ? " audio" :
146 (type == CODEC_TYPE_VIDEO) ? " video" : "",
147 fmt->filename);
148 return -1;
151 ctx = fmt->streams[i]->codec;
153 if (!(*codec = avcodec_find_decoder(ctx->codec_id))) {
154 logger.write(LOG_DEBUG, FFMPEG_MODULE,
155 "Failed to find decoder for %s", ctx->codec_name);
156 return -1;
159 if (avcodec_open(ctx, *codec) < 0) {
160 logger.write(LOG_DEBUG, FFMPEG_MODULE,
161 "Failed to open decoder %s", (*codec)->name);
162 return -1;
165 return i;
168 /* Attempt to open a VFS resource with FFmpeg */
169 static int ff_av_open(const char *resource, int type,
170 const struct audio_format *afmt,
171 const struct video_format *vfmt)
173 char *url;
174 struct resource r = { .astream = -1, .vstream = -1 };
175 int i;
177 if (!(type & AV_INPUT_AUDIO || type & AV_INPUT_VIDEO))
178 return -1;
180 if (!(url = malloc(strlen("vfs:") + strlen(resource) + 1)))
181 return -1;
183 /* Find a free resource, or make one */
184 for (i = 0; i < datasz; i++)
185 if (data[i].fmt == NULL)
186 break;
187 if (i >= datasz)
188 if (moreresources())
189 goto err;
191 strcpy(url, "vfs:");
192 strcat(url, resource);
194 if (av_open_input_file(&r.fmt, url, NULL, 0, NULL) != 0) {
195 logger.write(LOG_DEBUG, FFMPEG_MODULE,
196 "Failed to open %s", url);
197 goto err;
200 if (av_find_stream_info(r.fmt) < 0) {
201 logger.write(LOG_DEBUG, FFMPEG_MODULE,
202 "Failed to find stream info for %s", url);
203 goto err;
206 if (type & AV_INPUT_AUDIO) {
207 r.astream = getstream(CODEC_TYPE_AUDIO, &r.acodec, r.fmt);
208 if (r.astream == -1)
209 goto err;
210 r.actx = r.fmt->streams[r.astream]->codec;
211 r.afmt = *afmt;
212 if (!(r.aframe = malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE)))
213 goto err;
215 if (type & AV_INPUT_VIDEO) {
216 r.vstream = getstream(CODEC_TYPE_VIDEO, &r.vcodec, r.fmt);
217 if (r.vstream == -1)
218 goto err;
219 r.vctx = r.fmt->streams[r.vstream]->codec;
222 data[i] = r;
223 return i;
224 err:
225 if (r.actx)
226 avcodec_close(r.actx);
227 if (r.vctx)
228 avcodec_close(r.vctx);
229 if (r.fmt)
230 av_close_input_file(r.fmt);
232 free(r.aframe);
234 free(url);
235 return -1;
238 /* Close an open resource */
239 static void ff_av_close(int id)
241 if (id >= datasz || !data[id].fmt)
242 return;
244 if (data[id].actx)
245 avcodec_close(data[id].actx);
246 if (data[id].vctx)
247 avcodec_close(data[id].vctx);
249 av_close_input_file(data[id].fmt);
250 data[id].fmt = NULL;
252 free(data[id].aframe);
254 return;
257 /* Read decoded audio data */
258 static int ff_av_read_audio(int id, void *buf, int len)
260 struct resource *d;
261 int written = 0;
262 char *stream = buf;
264 if (id >= datasz || !data[id].fmt || data[id].astream == -1)
265 return -1;
267 d = &data[id];
269 if (d->aframeoff < d->aframelen) {
270 if (d->aframelen - d->aframeoff >= len) {
271 memcpy(stream, d->aframe+(d->aframeoff/2), len);
272 d->aframeoff += len;
273 written += len;
274 return written;
275 } else {
276 memcpy(stream, d->aframe+(d->aframeoff/2),
277 d->aframelen - d->aframeoff);
279 written += d->aframelen - d->aframeoff;
280 len -= d->aframelen - d->aframeoff;
281 stream += d->aframelen - d->aframeoff;
285 while (len > 0) {
286 AVPacket packet;
287 d->aframelen = 0, d->aframeoff = 0;
288 if (av_read_frame(d->fmt, &packet) < 0)
289 break;
291 if (packet.stream_index == d->astream) {
292 d->aframelen = AVCODEC_MAX_AUDIO_FRAME_SIZE;
293 avcodec_decode_audio2(d->actx, d->aframe, &d->aframelen,
294 packet.data, packet.size);
296 if (d->aframelen <= 0)
297 return av_free_packet(&packet), written;
299 memcpy(stream, d->aframe, MIN(len, d->aframelen));
301 d->aframeoff += MIN(len, d->aframelen);
302 stream += MIN(len, d->aframelen);
303 written += MIN(len, d->aframelen);
304 len -= MIN(len, d->aframelen);
307 av_free_packet(&packet);
310 return written;
313 static int ff_av_rewind(int id)
315 if (id >= datasz || !data[id].fmt)
316 return -1;
318 if (av_seek_frame(data[id].fmt, -1, 0, AVSEEK_FLAG_BYTE) < 0)
319 return -1;
321 return 0;
324 int init(plugin_useservice_t useservice)
326 static struct av_accessor av = {
327 .open = ff_av_open,
328 .close = ff_av_close,
329 .read_audio = ff_av_read_audio,
330 .rewind = ff_av_rewind,
333 if (useservice(LOG_SERVICE, FFMPEG_MODULE, &logger))
334 return -1;
336 if (useservice(VFSACCESS_SERVICE, FFMPEG_MODULE, &vfs))
337 return -1;
339 if (useservice(AV_PLUGIN_SERVICE, FFMPEG_MODULE, &av))
340 return -1;
342 if (moreresources())
343 return -1;
345 av_register_all();
346 register_protocol(&vfs_protocol);
348 logger.write(LOG_INFO, FFMPEG_MODULE,
349 "libavformat/libavcodec media input " FFMPEG_MOD_VERSION);
351 return 0;